Fun 0.41.5
The programming language that makes You have fun
Loading...
Searching...
No Matches
parser.c
Go to the documentation of this file.
1/*
2 * This file is part of the Fun programming language.
3 * https://fun-lang.xyz/
4 *
5 * Copyright 2025 Johannes Findeisen <you@hanez.org>
6 * Licensed under the terms of the Apache-2.0 license.
7 * https://opensource.org/license/apache-2-0
8 */
9
45
46#include "parser.h"
47#include "value.h"
48#include "vm.h"
49#include <ctype.h>
50#include <stdarg.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54
55/* from parser_utils.c */
56extern char *preprocess_includes_with_path(const char *src, const char *current_path);
57
58/* Forward declarations for helpers used before their definitions */
59static void skip_to_eol(const char *src, size_t len, size_t *pos);
60static int read_line_start(const char *src, size_t len, size_t *pos, int *out_indent);
61static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos, int current_indent);
62
63/* ---- parser error state ---- */
64static const char *g_current_source_path = NULL; /* for propagating filename into nested bytecodes */
65static int g_has_error = 0;
66static size_t g_err_pos = 0;
67static char g_err_msg[256];
68static int g_err_line = 0;
69static int g_err_col = 0;
70
71/* ---- compiler-generated temporary counter ---- */
72static int g_temp_counter = 0;
73
74/* ---- runtime debug control (for suppressing noisy stdout in production/CGI) ---- */
84static int env_truthy(const char *name) {
85 const char *v = getenv(name);
86 if (!v) return 0;
87 if (strcmp(v, "1") == 0) return 1;
88 if (strcmp(v, "true") == 0) return 1;
89 if (strcmp(v, "TRUE") == 0) return 1;
90 if (strcmp(v, "yes") == 0) return 1;
91 if (strcmp(v, "YES") == 0) return 1;
92 if (strcmp(v, "on") == 0) return 1;
93 if (strcmp(v, "ON") == 0) return 1;
94 return 0;
95}
96
105static int fun_debug_enabled(void) {
106 /* Allow enabling debug dumps at runtime via environment.
107 * Default is OFF to avoid contaminating stdout (e.g., CGI responses).
108 */
109 if (env_truthy("FUN_TRACE")) return 1;
110 if (env_truthy("FUN_DEBUG")) return 1;
111 return 0;
112}
113
114/* Declared type metadata encoding in types[]:
115 0 = dynamic/untyped;
116 positive/negative 8/16/32/64 = integers (negative means signed);
117 TYPE_META_STRING/BOOLEAN/NIL mark non-integer enforced types;
118 TYPE_META_CLASS marks class instances (Map with "__class"). */
120#define TYPE_META_STRING 10001
122#define TYPE_META_BOOLEAN 10002
124#define TYPE_META_NIL 10003
126#define TYPE_META_CLASS 10004
128#define TYPE_META_FLOAT 10005
130#define TYPE_META_ARRAY 10006
131
142static void parser_fail(size_t pos, const char *fmt, ...) {
143 g_has_error = 1;
144 g_err_pos = pos;
145 va_list ap;
146 va_start(ap, fmt);
147 vsnprintf(g_err_msg, sizeof(g_err_msg), fmt, ap);
148 va_end(ap);
149}
150
163static void calc_line_col(const char *src, size_t len, size_t pos, int *out_line, int *out_col) {
164 int line = 1, col = 1;
165 size_t limit = pos < len ? pos : len;
166 for (size_t i = 0; i < limit; ++i) {
167 if (src[i] == '\n') {
168 line++;
169 col = 1;
170 } else {
171 col++;
172 }
173 }
174 if (out_line) *out_line = line;
175 if (out_col) *out_col = col;
176}
177
178/* --------------------------- */
179
180/* Namespace alias tracking (for include-as):
181 We scan preprocessed source for lines starting with "// __ns_alias__: <name>"
182 and treat dot-calls on those identifiers as plain function calls (no implicit 'this'). */
183static char *g_ns_aliases[64];
184static int g_ns_alias_count = 0;
185
189static void ns_aliases_reset(void) {
190 for (int i = 0; i < g_ns_alias_count; ++i) {
191 free(g_ns_aliases[i]);
192 g_ns_aliases[i] = NULL;
193 }
194 g_ns_alias_count = 0;
195}
196
207static void ns_aliases_scan(const char *src, size_t len) {
208 const char *marker = "// __ns_alias__: ";
209 size_t mlen = strlen(marker);
210 size_t i = 0;
211 while (i < len) {
212 /* find start of line */
213 size_t ls = i;
214 /* move to end of line first */
215 while (i < len && src[i] != '\n')
216 i++;
217 size_t le = i;
218 /* include trailing '\n' in next iteration */
219 if (i < len && src[i] == '\n') i++;
220
221 if (le - ls >= mlen && strncmp(src + ls, marker, mlen) == 0) {
222 size_t p = ls + mlen;
223 /* read identifier */
224 size_t start = p;
225 while (p < le && (src[p] == ' ' || src[p] == '\t'))
226 p++;
227 if (p < le && (isalpha((unsigned char)src[p]) || src[p] == '_')) {
228 size_t q = p + 1;
229 while (q < le && (isalnum((unsigned char)src[q]) || src[q] == '_'))
230 q++;
231 size_t n = q - p;
232 if (n > 0 && g_ns_alias_count < (int)(sizeof(g_ns_aliases) / sizeof(g_ns_aliases[0]))) {
233 char *name = (char *)malloc(n + 1);
234 if (name) {
235 memcpy(name, src + p, n);
236 name[n] = '\0';
237 g_ns_aliases[g_ns_alias_count++] = name;
238 }
239 }
240 }
241 }
242 }
243}
244
251static int is_ns_alias(const char *name) {
252 if (!name) return 0;
253 for (int i = 0; i < g_ns_alias_count; ++i) {
254 if (strcmp(g_ns_aliases[i], name) == 0) return 1;
255 }
256 return 0;
257}
258
259#include "parser_utils.c"
260
261/* very small global symbol table for LOAD_GLOBAL/STORE_GLOBAL */
262static struct {
264 int types[MAX_GLOBALS]; /* 0=untyped/number default; else bit width: 8/16/32/64; negative for signed */
265 int is_class[MAX_GLOBALS]; /* 1 if this global name denotes a class factory */
266 int count;
267} G = {{0}, {0}, {0}, 0};
268
275static int sym_find(const char *name) {
276 for (int i = 0; i < G.count; ++i) {
277 if (strcmp(G.names[i], name) == 0) return i;
278 }
279 return -1;
280}
281
292static int sym_index(const char *name) {
293 int existing = sym_find(name);
294 if (existing >= 0) return existing;
295 if (G.count >= MAX_GLOBALS) {
296 parser_fail(0, "Too many globals (max %d)", MAX_GLOBALS);
297 return 0;
298 }
299 G.names[G.count] = strdup(name);
300 G.types[G.count] = 0; /* default: untyped */
301 G.is_class[G.count] = 0; /* default: not a class */
302 return G.count++;
303}
304
305/* ---- locals environment for functions ---- */
306typedef struct {
308 int types[MAX_FRAME_LOCALS]; /* 0=untyped; else bit width: 8/16/32/64 */
309 int count;
310} LocalEnv;
311
312static LocalEnv *g_locals = NULL;
313
314/* --- nested function environment tracking (for no-capture enforcement) --- */
315static LocalEnv *g_func_env_stack[64];
316static int g_func_env_depth = 0; /* number of valid outer env entries */
317
326static int name_in_outer_envs(const char *name) {
327 if (g_func_env_depth <= 0) return 0;
328 for (int d = g_func_env_depth - 1; d >= 0; --d) {
329 LocalEnv *e = g_func_env_stack[d];
330 if (!e) continue;
331 for (int i = 0; i < e->count; ++i) {
332 if (e->names[i] && strcmp(e->names[i], name) == 0) return 1;
333 }
334 }
335 return 0;
336}
337
338/* loop context for break/continue patching */
339typedef struct LoopCtx {
340 int break_jumps[64];
344 struct LoopCtx *prev;
346
347static LoopCtx *g_loop_ctx = NULL;
348
355static int local_find(const char *name) {
356 if (!g_locals) return -1;
357 for (int i = 0; i < g_locals->count; ++i) {
358 if (strcmp(g_locals->names[i], name) == 0) return i;
359 }
360 return -1;
361}
362
369static int local_add(const char *name) {
370 if (!g_locals) return -1;
371 if (g_locals->count >= MAX_FRAME_LOCALS) {
372 parser_fail(0, "Too many local variables/parameters (max %d)", MAX_FRAME_LOCALS);
373 return -1;
374 }
375 int idx = g_locals->count++;
376 g_locals->names[idx] = strdup(name);
377 return idx;
378}
379
380/* forward declaration so helpers can recurse */
381static int emit_expression(Bytecode *bc, const char *src, size_t len, size_t *pos);
382
383/* primary: (expr) | string | number | true/false | identifier */
396static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) {
397 skip_spaces(src, len, pos);
398
399 /* anonymous function literal: fn(arg, ...) <newline> indented-body end */
400 if (*pos + 2 < len && starts_with(src, len, *pos, "fn") && (src[*pos + 2] == '(' || src[*pos + 2] == ' ' || src[*pos + 2] == '\t')) {
401 *pos += 2;
402 skip_spaces(src, len, pos);
403 if (!consume_char(src, len, pos, '(')) {
404 parser_fail(*pos, "Expected '(' after 'fn'");
405 return 0;
406 }
407
408 /* build locals from parameters */
409 LocalEnv env = {{0}, {0}, 0};
410 LocalEnv *prev = g_locals;
411 int saved_depth = g_func_env_depth;
412 /* push outer env for no-capture checks */
413 if (prev != NULL) {
414 if (g_func_env_depth >= (int)(sizeof(g_func_env_stack) / sizeof(g_func_env_stack[0]))) {
415 parser_fail(*pos, "Too many nested functions (env stack overflow)");
416 return 0;
417 }
418 g_func_env_stack[g_func_env_depth++] = prev;
419 }
420 g_locals = &env;
421
422 skip_spaces(src, len, pos);
423 if (*pos < len && src[*pos] != ')') {
424 for (;;) {
425 char *pname = NULL;
426 if (!read_identifier_into(src, len, pos, &pname)) {
427 parser_fail(*pos, "Expected parameter name");
428 g_locals = prev;
429 g_func_env_depth = saved_depth;
430 return 0;
431 }
432 if (local_find(pname) >= 0) {
433 parser_fail(*pos, "Duplicate parameter name '%s'", pname);
434 free(pname);
435 g_locals = prev;
436 g_func_env_depth = saved_depth;
437 return 0;
438 }
439 local_add(pname);
440 free(pname);
441 skip_spaces(src, len, pos);
442 if (*pos < len && src[*pos] == ',') {
443 (*pos)++;
444 skip_spaces(src, len, pos);
445 continue;
446 }
447 break;
448 }
449 }
450 if (!consume_char(src, len, pos, ')')) {
451 parser_fail(*pos, "Expected ')' after parameter list");
452 g_locals = prev;
453 g_func_env_depth = saved_depth;
454 return 0;
455 }
456 /* end header line */
457 skip_to_eol(src, len, pos);
458
459 /* compile body into separate Bytecode */
460 Bytecode *fn_bc = bytecode_new();
461 if (fn_bc) {
462 if (fn_bc->name) free((void *)fn_bc->name);
463 fn_bc->name = strdup("<fn>");
464 if (fn_bc->source_file) free((void *)fn_bc->source_file);
465 if (g_current_source_path) fn_bc->source_file = strdup(g_current_source_path);
466 }
467
468 /* parse body at increased indent if present */
469 int body_indent = 0;
470 size_t look_body = *pos;
471 if (read_line_start(src, len, &look_body, &body_indent) && body_indent > 0) {
472 parse_block(fn_bc, src, len, pos, body_indent);
473 } else {
474 /* empty body ok */
475 }
477
478 /* restore env stack */
479 g_locals = prev;
480 g_func_env_depth = saved_depth;
481
482 int fci = bytecode_add_constant(bc, make_function(fn_bc));
484 return 1;
485 }
486
487 /* parenthesized */
488 if (*pos < len && src[*pos] == '(') {
489 (*pos)++; /* '(' */
490 if (!emit_expression(bc, src, len, pos)) {
491 parser_fail(*pos, "Expected expression after '('");
492 return 0;
493 }
494 if (!consume_char(src, len, pos, ')')) {
495 parser_fail(*pos, "Expected ')'");
496 return 0;
497 }
498 /* postfix indexing or slice */
499 for (;;) {
500 skip_spaces(src, len, pos);
501 if (*pos < len && src[*pos] == '[') {
502 (*pos)++;
503 if (!emit_expression(bc, src, len, pos)) {
504 parser_fail(*pos, "Expected start expression");
505 return 0;
506 }
507 skip_spaces(src, len, pos);
508 if (*pos < len && src[*pos] == ':') {
509 (*pos)++;
510 skip_spaces(src, len, pos);
511 size_t savep4 = *pos;
512 if (!emit_expression(bc, src, len, pos)) {
513 *pos = savep4;
514 int ci4 = bytecode_add_constant(bc, make_int(-1));
516 }
517 if (!consume_char(src, len, pos, ']')) {
518 parser_fail(*pos, "Expected ']' after slice");
519 return 0;
520 }
522 continue;
523 } else {
524 if (!consume_char(src, len, pos, ']')) {
525 parser_fail(*pos, "Expected ']' after index");
526 return 0;
527 }
529 continue;
530 }
531 }
532 break;
533 }
534 return 1;
535 }
536
537 /* string */
538 char *s = parse_string_literal_any_quote(src, len, pos);
539 if (s) {
540 int ci = bytecode_add_constant(bc, make_string(s));
541 free(s);
543 /* postfix indexing or slice */
544 for (;;) {
545 skip_spaces(src, len, pos);
546 if (*pos < len && src[*pos] == '[') {
547 (*pos)++;
548 if (!emit_expression(bc, src, len, pos)) {
549 parser_fail(*pos, "Expected start expression");
550 return 0;
551 }
552 skip_spaces(src, len, pos);
553 if (*pos < len && src[*pos] == ':') {
554 (*pos)++;
555 skip_spaces(src, len, pos);
556 /* end is optional; if missing, use -1 (till end) */
557 int has_end = 0;
558 size_t savep = *pos;
559 if (emit_expression(bc, src, len, pos)) {
560 has_end = 1;
561 } else {
562 *pos = savep;
563 int ci = bytecode_add_constant(bc, make_int(-1));
565 }
566 if (!consume_char(src, len, pos, ']')) {
567 parser_fail(*pos, "Expected ']' after slice");
568 return 0;
569 }
571 continue;
572 } else {
573 if (!consume_char(src, len, pos, ']')) {
574 parser_fail(*pos, "Expected ']' after index");
575 return 0;
576 }
578 continue;
579 }
580 }
581 break;
582 }
583 return 1;
584 }
585
586 /* array literal: [expr, expr, ...] */
587 skip_spaces(src, len, pos);
588 if (*pos < len && src[*pos] == '[') {
589 (*pos)++; /* '[' */
590 int count = 0;
591 skip_spaces(src, len, pos);
592 if (*pos < len && src[*pos] != ']') {
593 for (;;) {
594 if (!emit_expression(bc, src, len, pos)) {
595 parser_fail(*pos, "Expected expression in array literal");
596 return 0;
597 }
598 count++;
599 skip_spaces(src, len, pos);
600 if (*pos < len && src[*pos] == ',') {
601 (*pos)++;
602 skip_spaces(src, len, pos);
603 continue;
604 }
605 break;
606 }
607 }
608 if (!consume_char(src, len, pos, ']')) {
609 parser_fail(*pos, "Expected ']' to close array literal");
610 return 0;
611 }
613 /* postfix indexing or slice */
614 for (;;) {
615 skip_spaces(src, len, pos);
616 if (*pos < len && src[*pos] == '[') {
617 (*pos)++;
618 if (!emit_expression(bc, src, len, pos)) {
619 parser_fail(*pos, "Expected start expression");
620 return 0;
621 }
622 skip_spaces(src, len, pos);
623 if (*pos < len && src[*pos] == ':') {
624 (*pos)++;
625 skip_spaces(src, len, pos);
626 size_t savep3 = *pos;
627 if (!emit_expression(bc, src, len, pos)) {
628 *pos = savep3;
629 int ci3 = bytecode_add_constant(bc, make_int(-1));
631 }
632 if (!consume_char(src, len, pos, ']')) {
633 parser_fail(*pos, "Expected ']' after slice");
634 return 0;
635 }
637 continue;
638 } else {
639 if (!consume_char(src, len, pos, ']')) {
640 parser_fail(*pos, "Expected ']' after index");
641 return 0;
642 }
644 continue;
645 }
646 }
647 break;
648 }
649 return 1;
650 }
651
652 /* map literal: { "key": expr, ... } */
653 skip_spaces(src, len, pos);
654 if (*pos < len && src[*pos] == '{') {
655 (*pos)++; /* '{' */
656 int pairs = 0;
657 skip_spaces(src, len, pos);
658 if (*pos < len && src[*pos] != '}') {
659 for (;;) {
660 /* key must be a string literal */
661 char *k = parse_string_literal_any_quote(src, len, pos);
662 if (!k) {
663 parser_fail(*pos, "Expected string key in map literal");
664 return 0;
665 }
666 int kci = bytecode_add_constant(bc, make_string(k));
667 free(k);
669 skip_spaces(src, len, pos);
670 if (!consume_char(src, len, pos, ':')) {
671 parser_fail(*pos, "Expected ':' after map key");
672 return 0;
673 }
674 if (!emit_expression(bc, src, len, pos)) {
675 parser_fail(*pos, "Expected value expression in map literal");
676 return 0;
677 }
678 pairs++;
679 skip_spaces(src, len, pos);
680 if (*pos < len && src[*pos] == ',') {
681 (*pos)++;
682 skip_spaces(src, len, pos);
683 continue;
684 }
685 break;
686 }
687 }
688 if (!consume_char(src, len, pos, '}')) {
689 parser_fail(*pos, "Expected '}' to close map literal");
690 return 0;
691 }
693 return 1;
694 }
695
696 /* number (prefer float first to consume cases like 1.23 or 1e2) */
697 int ok = 0;
698 size_t save = *pos;
699 /* float literal */
700 double fval = parse_float_literal_value(src, len, pos, &ok);
701 if (ok) {
702 int ci = bytecode_add_constant(bc, make_float(fval));
704 /* postfix indexing or slice (not typical for floats but keep consistency) */
705 for (;;) {
706 skip_spaces(src, len, pos);
707 if (*pos < len && src[*pos] == '[') {
708 (*pos)++;
709 if (!emit_expression(bc, src, len, pos)) {
710 parser_fail(*pos, "Expected start expression");
711 return 0;
712 }
713 skip_spaces(src, len, pos);
714 if (*pos < len && src[*pos] == ':') {
715 (*pos)++;
716 skip_spaces(src, len, pos);
717 size_t savep2 = *pos;
718 if (!emit_expression(bc, src, len, pos)) {
719 *pos = savep2;
720 int ci2 = bytecode_add_constant(bc, make_int(-1));
722 }
723 if (!consume_char(src, len, pos, ']')) {
724 parser_fail(*pos, "Expected ']' after slice");
725 return 0;
726 }
728 continue;
729 } else {
730 if (!consume_char(src, len, pos, ']')) {
731 parser_fail(*pos, "Expected ']' after index");
732 return 0;
733 }
735 continue;
736 }
737 }
738 break;
739 }
740 return 1;
741 }
742 *pos = save;
743 /* integer literal */
744 int64_t ival = parse_int_literal_value(src, len, pos, &ok);
745 if (ok) {
746 int ci = bytecode_add_constant(bc, make_int(ival));
748 /* postfix indexing or slice */
749 for (;;) {
750 skip_spaces(src, len, pos);
751 if (*pos < len && src[*pos] == '[') {
752 (*pos)++;
753 if (!emit_expression(bc, src, len, pos)) {
754 parser_fail(*pos, "Expected start expression");
755 return 0;
756 }
757 skip_spaces(src, len, pos);
758 if (*pos < len && src[*pos] == ':') {
759 (*pos)++;
760 skip_spaces(src, len, pos);
761 int has_end = 0;
762 size_t savep2 = *pos;
763 if (emit_expression(bc, src, len, pos)) {
764 has_end = 1;
765 } else {
766 *pos = savep2;
767 int ci2 = bytecode_add_constant(bc, make_int(-1));
769 }
770 if (!consume_char(src, len, pos, ']')) {
771 parser_fail(*pos, "Expected ']' after slice");
772 return 0;
773 }
775 continue;
776 } else {
777 if (!consume_char(src, len, pos, ']')) {
778 parser_fail(*pos, "Expected ']' after index");
779 return 0;
780 }
782 continue;
783 }
784 }
785 break;
786 }
787 return 1;
788 }
789
790 /* identifier or keyword */
791 char *name = NULL;
792 if (read_identifier_into(src, len, pos, &name)) {
793 if (strcmp(name, "true") == 0 || strcmp(name, "false") == 0) {
794 int ci = bytecode_add_constant(bc, make_bool(strcmp(name, "true") == 0 ? 1 : 0));
795 free(name);
797 return 1;
798 }
799
800 /* call or variable load (locals preferred) */
801 skip_spaces(src, len, pos);
802 int local_idx = local_find(name);
803 int is_call = (*pos < len && src[*pos] == '(');
804
805 if (is_call) {
806 /* builtins */
807 if (strcmp(name, "len") == 0) {
808 (*pos)++; /* '(' */
809 if (!emit_expression(bc, src, len, pos)) {
810 parser_fail(*pos, "len expects 1 argument");
811 free(name);
812 return 0;
813 }
814 if (!consume_char(src, len, pos, ')')) {
815 parser_fail(*pos, "Expected ')' after len arg");
816 free(name);
817 return 0;
818 }
820 free(name);
821 return 1;
822 }
823 if (strcmp(name, "push") == 0) {
824 (*pos)++; /* '(' */
825 if (!emit_expression(bc, src, len, pos)) {
826 parser_fail(*pos, "push expects array");
827 free(name);
828 return 0;
829 }
830 if (*pos < len && src[*pos] == ',') {
831 (*pos)++;
832 skip_spaces(src, len, pos);
833 } else {
834 parser_fail(*pos, "push expects 2 args");
835 free(name);
836 return 0;
837 }
838 if (!emit_expression(bc, src, len, pos)) {
839 parser_fail(*pos, "push expects value");
840 free(name);
841 return 0;
842 }
843 if (!consume_char(src, len, pos, ')')) {
844 parser_fail(*pos, "Expected ')' after push args");
845 free(name);
846 return 0;
847 }
849 free(name);
850 return 1;
851 }
852 if (strcmp(name, "pop") == 0) {
853 (*pos)++; /* '(' */
854 if (!emit_expression(bc, src, len, pos)) {
855 parser_fail(*pos, "pop expects array");
856 free(name);
857 return 0;
858 }
859 if (!consume_char(src, len, pos, ')')) {
860 parser_fail(*pos, "Expected ')' after pop arg");
861 free(name);
862 return 0;
863 }
865 free(name);
866 return 1;
867 }
868 if (strcmp(name, "set") == 0) {
869 (*pos)++; /* '(' */
870 if (!emit_expression(bc, src, len, pos)) {
871 parser_fail(*pos, "set expects array");
872 free(name);
873 return 0;
874 }
875 if (*pos < len && src[*pos] == ',') {
876 (*pos)++;
877 skip_spaces(src, len, pos);
878 } else {
879 parser_fail(*pos, "set expects 3 args");
880 free(name);
881 return 0;
882 }
883 if (!emit_expression(bc, src, len, pos)) {
884 parser_fail(*pos, "set expects index");
885 free(name);
886 return 0;
887 }
888 if (*pos < len && src[*pos] == ',') {
889 (*pos)++;
890 skip_spaces(src, len, pos);
891 } else {
892 parser_fail(*pos, "set expects 3 args");
893 free(name);
894 return 0;
895 }
896 if (!emit_expression(bc, src, len, pos)) {
897 parser_fail(*pos, "set expects value");
898 free(name);
899 return 0;
900 }
901 if (!consume_char(src, len, pos, ')')) {
902 parser_fail(*pos, "Expected ')' after set args");
903 free(name);
904 return 0;
905 }
907 free(name);
908 return 1;
909 }
910 if (strcmp(name, "insert") == 0) {
911 (*pos)++; /* '(' */
912 if (!emit_expression(bc, src, len, pos)) {
913 parser_fail(*pos, "insert expects array");
914 free(name);
915 return 0;
916 }
917 if (*pos < len && src[*pos] == ',') {
918 (*pos)++;
919 skip_spaces(src, len, pos);
920 } else {
921 parser_fail(*pos, "insert expects 3 args");
922 free(name);
923 return 0;
924 }
925 if (!emit_expression(bc, src, len, pos)) {
926 parser_fail(*pos, "insert expects index");
927 free(name);
928 return 0;
929 }
930 if (*pos < len && src[*pos] == ',') {
931 (*pos)++;
932 skip_spaces(src, len, pos);
933 } else {
934 parser_fail(*pos, "insert expects 3 args");
935 free(name);
936 return 0;
937 }
938 if (!emit_expression(bc, src, len, pos)) {
939 parser_fail(*pos, "insert expects value");
940 free(name);
941 return 0;
942 }
943 if (!consume_char(src, len, pos, ')')) {
944 parser_fail(*pos, "Expected ')' after insert args");
945 free(name);
946 return 0;
947 }
949 free(name);
950 return 1;
951 }
952 if (strcmp(name, "remove") == 0) {
953 (*pos)++; /* '(' */
954 if (!emit_expression(bc, src, len, pos)) {
955 parser_fail(*pos, "remove expects array");
956 free(name);
957 return 0;
958 }
959 if (*pos < len && src[*pos] == ',') {
960 (*pos)++;
961 skip_spaces(src, len, pos);
962 } else {
963 parser_fail(*pos, "remove expects 2 args");
964 free(name);
965 return 0;
966 }
967 if (!emit_expression(bc, src, len, pos)) {
968 parser_fail(*pos, "remove expects index");
969 free(name);
970 return 0;
971 }
972 if (!consume_char(src, len, pos, ')')) {
973 parser_fail(*pos, "Expected ')' after remove args");
974 free(name);
975 return 0;
976 }
978 free(name);
979 return 1;
980 }
981 if (strcmp(name, "to_number") == 0) {
982 (*pos)++; /* '(' */
983 if (!emit_expression(bc, src, len, pos)) {
984 parser_fail(*pos, "to_number expects 1 argument");
985 free(name);
986 return 0;
987 }
988 if (!consume_char(src, len, pos, ')')) {
989 parser_fail(*pos, "Expected ')' after to_number arg");
990 free(name);
991 return 0;
992 }
994 free(name);
995 return 1;
996 }
997 if (strcmp(name, "to_string") == 0) {
998 (*pos)++; /* '(' */
999 if (!emit_expression(bc, src, len, pos)) {
1000 parser_fail(*pos, "to_string expects 1 argument");
1001 free(name);
1002 return 0;
1003 }
1004 if (!consume_char(src, len, pos, ')')) {
1005 parser_fail(*pos, "Expected ')' after to_string arg");
1006 free(name);
1007 return 0;
1008 }
1010 free(name);
1011 return 1;
1012 }
1013 if (strcmp(name, "cast") == 0) {
1014 (*pos)++; /* '(' */
1015 /* cast(value, typeName) */
1016 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
1017 parser_fail(*pos, "cast expects (value, typeName)");
1018 free(name);
1019 return 0;
1020 }
1021 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
1022 parser_fail(*pos, "cast expects (value, typeName)");
1023 free(name);
1024 return 0;
1025 }
1027 free(name);
1028 return 1;
1029 }
1030 if (strcmp(name, "typeof") == 0) {
1031 (*pos)++; /* '(' */
1032 /* Special handling for typeof(<identifier>) to return declared subtype for integers */
1033 size_t peek = *pos;
1034 char *vname = NULL;
1035 int handled = 0;
1036 if (read_identifier_into(src, len, &peek, &vname)) {
1037 skip_spaces(src, len, &peek);
1038 if (peek < len && src[peek] == ')') {
1039 int meta = 0;
1040 int lidx = local_find(vname);
1041 if (lidx >= 0) {
1042 meta = g_locals->types[lidx];
1043 } else {
1044 int gi = sym_index(vname);
1045 if (gi >= 0) meta = G.types[gi];
1046 }
1047
1048 if (meta != 0 && meta != TYPE_META_STRING && meta != TYPE_META_BOOLEAN && meta != TYPE_META_NIL && meta != TYPE_META_CLASS && meta != TYPE_META_FLOAT) {
1049 /* Integer subtype: ±bits */
1050 int abs_bits = meta < 0 ? -meta : meta;
1051 const char *tname = (meta < 0)
1052 ? (abs_bits == 64 ? "Sint64" : (abs_bits == 32 ? "Sint32" : (abs_bits == 16 ? "Sint16" : "Sint8")))
1053 : (abs_bits == 64 ? "Uint64" : (abs_bits == 32 ? "Uint32" : (abs_bits == 16 ? "Uint16" : "Uint8")));
1056 *pos = peek + 1; /* consume name and ')' */
1057 handled = 1;
1058 }
1059 free(vname);
1060 } else {
1061 free(vname);
1062 }
1063 }
1064
1065 if (!handled) {
1066 /* General case: typeof(expression)
1067 If the value is a Map with "__class" key, return that string; else return base typeof.
1068 */
1069 if (!emit_expression(bc, src, len, pos)) {
1070 parser_fail(*pos, "typeof expects 1 argument");
1071 free(name);
1072 return 0;
1073 }
1074 if (!consume_char(src, len, pos, ')')) {
1075 parser_fail(*pos, "Expected ')' after typeof arg");
1076 free(name);
1077 return 0;
1078 }
1079
1080 /* [v] */
1081 bytecode_add_instruction(bc, OP_DUP, 0); /* [v, v] */
1082 bytecode_add_instruction(bc, OP_TYPEOF, 0); /* [v, tname] */
1083 {
1084 int ciMap = bytecode_add_constant(bc, make_string("Map"));
1085 bytecode_add_instruction(bc, OP_LOAD_CONST, ciMap); /* [v, tname, "Map"] */
1086 }
1087 bytecode_add_instruction(bc, OP_EQ, 0); /* [v, isMap] */
1088 int j_if_not_map = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
1089
1090 /* Map branch: [v] */
1091 bytecode_add_instruction(bc, OP_DUP, 0); /* [v, v] */
1092 {
1093 int kci = bytecode_add_constant(bc, make_string("__class"));
1094 bytecode_add_instruction(bc, OP_LOAD_CONST, kci); /* [v, v, "__class"] */
1095 }
1096 bytecode_add_instruction(bc, OP_HAS_KEY, 0); /* [v, has] */
1097 int j_no_meta = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
1098 /* has __class: try toString() -> return its result */
1099 bytecode_add_instruction(bc, OP_DUP, 0); /* [v, v] */
1100 {
1101 int kcits = bytecode_add_constant(bc, make_string("toString"));
1102 bytecode_add_instruction(bc, OP_LOAD_CONST, kcits); /* [v, v, "toString"] */
1103 }
1104 bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* [v, func] */
1105 bytecode_add_instruction(bc, OP_SWAP, 0); /* [func, v] */
1106 bytecode_add_instruction(bc, OP_CALL, 1); /* [string] */
1107 int j_end = bytecode_add_instruction(bc, OP_JUMP, 0);
1108
1109 /* no meta: drop v and return "Map" */
1110 bytecode_set_operand(bc, j_no_meta, bc->instr_count);
1111 bytecode_add_instruction(bc, OP_POP, 0); /* [] */
1112 {
1113 int ciMap2 = bytecode_add_constant(bc, make_string("Map"));
1114 bytecode_add_instruction(bc, OP_LOAD_CONST, ciMap2); /* ["Map"] */
1115 }
1116 int j_end2 = bytecode_add_instruction(bc, OP_JUMP, 0);
1117 int after_map = bc->instr_count;
1118
1119 /* not map: compute typeof(v) */
1120 bytecode_set_operand(bc, j_if_not_map, after_map);
1121 bytecode_add_instruction(bc, OP_TYPEOF, 0); /* [tname] */
1122
1123 /* end */
1124 bytecode_set_operand(bc, j_end, bc->instr_count);
1125 bytecode_set_operand(bc, j_end2, bc->instr_count);
1126 }
1127
1128 free(name);
1129 return 1;
1130 }
1131 if (strcmp(name, "keys") == 0) {
1132 (*pos)++; /* '(' */
1133 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
1134 parser_fail(*pos, "keys expects 1 arg");
1135 free(name);
1136 return 0;
1137 }
1139 free(name);
1140 return 1;
1141 }
1142 if (strcmp(name, "values") == 0) {
1143 (*pos)++; /* '(' */
1144 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
1145 parser_fail(*pos, "values expects 1 arg");
1146 free(name);
1147 return 0;
1148 }
1150 free(name);
1151 return 1;
1152 }
1153 if (strcmp(name, "has") == 0) {
1154 (*pos)++; /* '(' */
1155 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
1156 parser_fail(*pos, "has expects (map, key)");
1157 free(name);
1158 return 0;
1159 }
1160 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
1161 parser_fail(*pos, "has expects (map, key)");
1162 free(name);
1163 return 0;
1164 }
1166 free(name);
1167 return 1;
1168 }
1169 if (strcmp(name, "read_file") == 0) {
1170 (*pos)++; /* '(' */
1171 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
1172 parser_fail(*pos, "read_file expects 1 arg");
1173 free(name);
1174 return 0;
1175 }
1177 free(name);
1178 return 1;
1179 }
1180 if (strcmp(name, "write_file") == 0) {
1181 (*pos)++; /* '(' */
1182 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
1183 parser_fail(*pos, "write_file expects 2 args");
1184 free(name);
1185 return 0;
1186 }
1187 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
1188 parser_fail(*pos, "write_file expects 2 args");
1189 free(name);
1190 return 0;
1191 }
1193 free(name);
1194 return 1;
1195 }
1196 if (strcmp(name, "input") == 0) {
1197 (*pos)++; /* '(' */
1198 int hasPrompt = 0;
1199 skip_spaces(src, len, pos);
1200 if (*pos < len && src[*pos] != ')') {
1201 if (!emit_expression(bc, src, len, pos)) {
1202 parser_fail(*pos, "input expects 0 or 1 argument");
1203 free(name);
1204 return 0;
1205 }
1206 hasPrompt = 1;
1207 }
1208 if (!consume_char(src, len, pos, ')')) {
1209 parser_fail(*pos, "Expected ')' after input arg(s)");
1210 free(name);
1211 return 0;
1212 }
1213 bytecode_add_instruction(bc, OP_INPUT_LINE, hasPrompt ? 1 : 0);
1214 free(name);
1215 return 1;
1216 }
1217 if (strcmp(name, "input_hidden") == 0) {
1218 (*pos)++; /* '(' */
1219 int hasPrompt = 0;
1220 skip_spaces(src, len, pos);
1221 if (*pos < len && src[*pos] != ')') {
1222 if (!emit_expression(bc, src, len, pos)) {
1223 parser_fail(*pos, "input_hidden expects 0 or 1 argument");
1224 free(name);
1225 return 0;
1226 }
1227 hasPrompt = 1;
1228 }
1229 if (!consume_char(src, len, pos, ')')) {
1230 parser_fail(*pos, "Expected ')' after input_hidden arg(s)");
1231 free(name);
1232 return 0;
1233 }
1234 /* operand bit0 = hasPrompt, bit1 = hidden */
1235 bytecode_add_instruction(bc, OP_INPUT_LINE, (hasPrompt ? 1 : 0) | 2);
1236 free(name);
1237 return 1;
1238 }
1239 if (strcmp(name, "proc_run") == 0) {
1240 (*pos)++; /* '(' */
1241 if (!emit_expression(bc, src, len, pos)) {
1242 parser_fail(*pos, "proc_run expects 1 argument (command string)");
1243 free(name);
1244 return 0;
1245 }
1246 if (!consume_char(src, len, pos, ')')) {
1247 parser_fail(*pos, "Expected ')' after proc_run arg");
1248 free(name);
1249 return 0;
1250 }
1252 free(name);
1253 return 1;
1254 }
1255 if (strcmp(name, "system") == 0) {
1256 (*pos)++; /* '(' */
1257 if (!emit_expression(bc, src, len, pos)) {
1258 parser_fail(*pos, "system expects 1 argument (command string)");
1259 free(name);
1260 return 0;
1261 }
1262 if (!consume_char(src, len, pos, ')')) {
1263 parser_fail(*pos, "Expected ')' after system arg");
1264 free(name);
1265 return 0;
1266 }
1268 free(name);
1269 return 1;
1270 }
1271 if (strcmp(name, "time_now_ms") == 0) {
1272 (*pos)++; /* '(' */
1273 if (!consume_char(src, len, pos, ')')) {
1274 parser_fail(*pos, "time_now_ms expects ()");
1275 free(name);
1276 return 0;
1277 }
1279 free(name);
1280 return 1;
1281 }
1282 if (strcmp(name, "clock_mono_ms") == 0) {
1283 (*pos)++; /* '(' */
1284 if (!consume_char(src, len, pos, ')')) {
1285 parser_fail(*pos, "clock_mono_ms expects ()");
1286 free(name);
1287 return 0;
1288 }
1290 free(name);
1291 return 1;
1292 }
1293 if (strcmp(name, "date_format") == 0) {
1294 (*pos)++; /* '(' */
1295 if (!emit_expression(bc, src, len, pos)) {
1296 parser_fail(*pos, "date_format expects (ms:int, fmt:string)");
1297 free(name);
1298 return 0;
1299 }
1300 if (!consume_char(src, len, pos, ',')) {
1301 parser_fail(*pos, "date_format expects 2 args");
1302 free(name);
1303 return 0;
1304 }
1305 if (!emit_expression(bc, src, len, pos)) {
1306 parser_fail(*pos, "date_format expects (ms:int, fmt:string)");
1307 free(name);
1308 return 0;
1309 }
1310 if (!consume_char(src, len, pos, ')')) {
1311 parser_fail(*pos, "Expected ')' after date_format args");
1312 free(name);
1313 return 0;
1314 }
1316 free(name);
1317 return 1;
1318 }
1319 if (strcmp(name, "env") == 0) {
1320 (*pos)++; /* '(' */
1321 if (!emit_expression(bc, src, len, pos)) {
1322 parser_fail(*pos, "env expects 1 argument");
1323 free(name);
1324 return 0;
1325 }
1326 if (!consume_char(src, len, pos, ')')) {
1327 parser_fail(*pos, "Expected ')' after env arg");
1328 free(name);
1329 return 0;
1330 }
1332 free(name);
1333 return 1;
1334 }
1335 if (strcmp(name, "env_all") == 0) {
1336 (*pos)++; /* '(' */
1337 if (!consume_char(src, len, pos, ')')) {
1338 parser_fail(*pos, "env_all expects ()");
1339 free(name);
1340 return 0;
1341 }
1343 free(name);
1344 return 1;
1345 }
1346 if (strcmp(name, "fun_version") == 0) {
1347 (*pos)++; /* '(' */
1348 if (!consume_char(src, len, pos, ')')) {
1349 parser_fail(*pos, "fun_version expects ()");
1350 free(name);
1351 return 0;
1352 }
1354 free(name);
1355 return 1;
1356 }
1357 if (strcmp(name, "rust_hello") == 0) {
1358 (*pos)++; /* '(' */
1359 if (!consume_char(src, len, pos, ')')) {
1360 parser_fail(*pos, "rust_hello expects ()");
1361 free(name);
1362 return 0;
1363 }
1365 free(name);
1366 return 1;
1367 }
1368 if (strcmp(name, "rust_hello_args") == 0) {
1369 (*pos)++; /* '(' */
1370 if (!emit_expression(bc, src, len, pos)) {
1371 parser_fail(*pos, "rust_hello_args expects (message:string)");
1372 free(name);
1373 return 0;
1374 }
1375 if (!consume_char(src, len, pos, ')')) {
1376 parser_fail(*pos, "Expected ')' after rust_hello_args arg");
1377 free(name);
1378 return 0;
1379 }
1381 free(name);
1382 return 1;
1383 }
1384 if (strcmp(name, "rust_hello_args_return") == 0) {
1385 (*pos)++; /* '(' */
1386 if (!emit_expression(bc, src, len, pos)) {
1387 parser_fail(*pos, "rust_hello_args_return expects (message:string)");
1388 free(name);
1389 return 0;
1390 }
1391 if (!consume_char(src, len, pos, ')')) {
1392 parser_fail(*pos, "Expected ')' after rust_hello_args_return arg");
1393 free(name);
1394 return 0;
1395 }
1397 free(name);
1398 return 1;
1399 }
1400 if (strcmp(name, "rust_get_sp") == 0) {
1401 (*pos)++; /* '(' */
1402 if (!consume_char(src, len, pos, ')')) {
1403 parser_fail(*pos, "rust_get_sp expects ()");
1404 free(name);
1405 return 0;
1406 }
1408 free(name);
1409 return 1;
1410 }
1411 if (strcmp(name, "rust_set_exit") == 0) {
1412 (*pos)++; /* '(' */
1413 if (!emit_expression(bc, src, len, pos)) {
1414 parser_fail(*pos, "rust_set_exit expects (code:int)");
1415 free(name);
1416 return 0;
1417 }
1418 if (!consume_char(src, len, pos, ')')) {
1419 parser_fail(*pos, "Expected ')' after rust_set_exit arg");
1420 free(name);
1421 return 0;
1422 }
1424 free(name);
1425 return 1;
1426 }
1427 if (strcmp(name, "cpp_add") == 0) {
1428 (*pos)++; /* '(' */
1429 if (!emit_expression(bc, src, len, pos)) {
1430 parser_fail(*pos, "cpp_add expects (a:int, b:int)");
1431 free(name);
1432 return 0;
1433 }
1434 if (!consume_char(src, len, pos, ',')) {
1435 parser_fail(*pos, "cpp_add expects two arguments");
1436 free(name);
1437 return 0;
1438 }
1439 if (!emit_expression(bc, src, len, pos)) {
1440 parser_fail(*pos, "cpp_add expects (a:int, b:int)");
1441 free(name);
1442 return 0;
1443 }
1444 if (!consume_char(src, len, pos, ')')) {
1445 parser_fail(*pos, "Expected ')' after cpp_add args");
1446 free(name);
1447 return 0;
1448 }
1450 free(name);
1451 return 1;
1452 }
1453 if (strcmp(name, "os_list_dir") == 0) {
1454 (*pos)++; /* '(' */
1455 if (!emit_expression(bc, src, len, pos)) {
1456 parser_fail(*pos, "os_list_dir expects (path)");
1457 free(name);
1458 return 0;
1459 }
1460 if (!consume_char(src, len, pos, ')')) {
1461 parser_fail(*pos, "Expected ')' after os_list_dir arg");
1462 free(name);
1463 return 0;
1464 }
1466 free(name);
1467 return 1;
1468 }
1469 /* JSON builtins */
1470 if (strcmp(name, "json_parse") == 0) {
1471 (*pos)++; /* '(' */
1472 if (!emit_expression(bc, src, len, pos)) {
1473 parser_fail(*pos, "json_parse expects (text)");
1474 free(name);
1475 return 0;
1476 }
1477 if (!consume_char(src, len, pos, ')')) {
1478 parser_fail(*pos, "Expected ')' after json_parse arg");
1479 free(name);
1480 return 0;
1481 }
1483 free(name);
1484 return 1;
1485 }
1486 /* XML builtins (minimal) */
1487 if (strcmp(name, "xml_parse") == 0) {
1488 (*pos)++; /* '(' */
1489 if (!emit_expression(bc, src, len, pos)) {
1490 parser_fail(*pos, "xml_parse expects (text)");
1491 free(name);
1492 return 0;
1493 }
1494 if (!consume_char(src, len, pos, ')')) {
1495 parser_fail(*pos, "Expected ')' after xml_parse arg");
1496 free(name);
1497 return 0;
1498 }
1500 free(name);
1501 return 1;
1502 }
1503 if (strcmp(name, "xml_root") == 0) {
1504 (*pos)++; /* '(' */
1505 if (!emit_expression(bc, src, len, pos)) {
1506 parser_fail(*pos, "xml_root expects (doc_handle)");
1507 free(name);
1508 return 0;
1509 }
1510 if (!consume_char(src, len, pos, ')')) {
1511 parser_fail(*pos, "Expected ')' after xml_root arg");
1512 free(name);
1513 return 0;
1514 }
1516 free(name);
1517 return 1;
1518 }
1519 if (strcmp(name, "xml_name") == 0) {
1520 (*pos)++; /* '(' */
1521 if (!emit_expression(bc, src, len, pos)) {
1522 parser_fail(*pos, "xml_name expects (node_handle)");
1523 free(name);
1524 return 0;
1525 }
1526 if (!consume_char(src, len, pos, ')')) {
1527 parser_fail(*pos, "Expected ')' after xml_name arg");
1528 free(name);
1529 return 0;
1530 }
1532 free(name);
1533 return 1;
1534 }
1535 if (strcmp(name, "xml_text") == 0) {
1536 (*pos)++; /* '(' */
1537 if (!emit_expression(bc, src, len, pos)) {
1538 parser_fail(*pos, "xml_text expects (node_handle)");
1539 free(name);
1540 return 0;
1541 }
1542 if (!consume_char(src, len, pos, ')')) {
1543 parser_fail(*pos, "Expected ')' after xml_text arg");
1544 free(name);
1545 return 0;
1546 }
1548 free(name);
1549 return 1;
1550 }
1551 if (strcmp(name, "json_stringify") == 0) {
1552 (*pos)++; /* '(' */
1553 if (!emit_expression(bc, src, len, pos)) {
1554 parser_fail(*pos, "json_stringify expects (value, pretty)");
1555 free(name);
1556 return 0;
1557 }
1558 if (!consume_char(src, len, pos, ',')) {
1559 parser_fail(*pos, "json_stringify expects (value, pretty)");
1560 free(name);
1561 return 0;
1562 }
1563 if (!emit_expression(bc, src, len, pos)) {
1564 parser_fail(*pos, "json_stringify expects (value, pretty)");
1565 free(name);
1566 return 0;
1567 }
1568 if (!consume_char(src, len, pos, ')')) {
1569 parser_fail(*pos, "Expected ')' after json_stringify args");
1570 free(name);
1571 return 0;
1572 }
1574 free(name);
1575 return 1;
1576 }
1577 if (strcmp(name, "json_from_file") == 0) {
1578 (*pos)++; /* '(' */
1579 if (!emit_expression(bc, src, len, pos)) {
1580 parser_fail(*pos, "json_from_file expects (path)");
1581 free(name);
1582 return 0;
1583 }
1584 if (!consume_char(src, len, pos, ')')) {
1585 parser_fail(*pos, "Expected ')' after json_from_file arg");
1586 free(name);
1587 return 0;
1588 }
1590 free(name);
1591 return 1;
1592 }
1593 if (strcmp(name, "json_to_file") == 0) {
1594 (*pos)++; /* '(' */
1595 if (!emit_expression(bc, src, len, pos)) {
1596 parser_fail(*pos, "json_to_file expects (path, value, pretty)");
1597 free(name);
1598 return 0;
1599 }
1600 if (!consume_char(src, len, pos, ',')) {
1601 parser_fail(*pos, "json_to_file expects (path, value, pretty)");
1602 free(name);
1603 return 0;
1604 }
1605 if (!emit_expression(bc, src, len, pos)) {
1606 parser_fail(*pos, "json_to_file expects (path, value, pretty)");
1607 free(name);
1608 return 0;
1609 }
1610 if (!consume_char(src, len, pos, ',')) {
1611 parser_fail(*pos, "json_to_file expects (path, value, pretty)");
1612 free(name);
1613 return 0;
1614 }
1615 if (!emit_expression(bc, src, len, pos)) {
1616 parser_fail(*pos, "json_to_file expects (path, value, pretty)");
1617 free(name);
1618 return 0;
1619 }
1620 if (!consume_char(src, len, pos, ')')) {
1621 parser_fail(*pos, "Expected ')' after json_to_file args");
1622 free(name);
1623 return 0;
1624 }
1626 free(name);
1627 return 1;
1628 }
1629 /* INI (iniparser 4.2.6) builtins */
1630 if (strcmp(name, "ini_load") == 0) {
1631 (*pos)++; /* '(' */
1632 if (!emit_expression(bc, src, len, pos)) {
1633 parser_fail(*pos, "ini_load expects (path)");
1634 free(name);
1635 return 0;
1636 }
1637 if (!consume_char(src, len, pos, ')')) {
1638 parser_fail(*pos, "Expected ')' after ini_load arg");
1639 free(name);
1640 return 0;
1641 }
1643 free(name);
1644 return 1;
1645 }
1646 if (strcmp(name, "ini_free") == 0) {
1647 (*pos)++; /* '(' */
1648 if (!emit_expression(bc, src, len, pos)) {
1649 parser_fail(*pos, "ini_free expects (handle)");
1650 free(name);
1651 return 0;
1652 }
1653 if (!consume_char(src, len, pos, ')')) {
1654 parser_fail(*pos, "Expected ')' after ini_free arg");
1655 free(name);
1656 return 0;
1657 }
1659 free(name);
1660 return 1;
1661 }
1662 if (strcmp(name, "ini_get_string") == 0) {
1663 (*pos)++; /* '(' */
1664 if (!emit_expression(bc, src, len, pos)) {
1665 parser_fail(*pos, "ini_get_string expects (handle, section, key, default)");
1666 free(name);
1667 return 0;
1668 }
1669 if (!consume_char(src, len, pos, ',')) {
1670 parser_fail(*pos, "ini_get_string expects 4 args");
1671 free(name);
1672 return 0;
1673 }
1674 if (!emit_expression(bc, src, len, pos)) {
1675 parser_fail(*pos, "ini_get_string expects 4 args");
1676 free(name);
1677 return 0;
1678 }
1679 if (!consume_char(src, len, pos, ',')) {
1680 parser_fail(*pos, "ini_get_string expects 4 args");
1681 free(name);
1682 return 0;
1683 }
1684 if (!emit_expression(bc, src, len, pos)) {
1685 parser_fail(*pos, "ini_get_string expects 4 args");
1686 free(name);
1687 return 0;
1688 }
1689 if (!consume_char(src, len, pos, ',')) {
1690 parser_fail(*pos, "ini_get_string expects 4 args");
1691 free(name);
1692 return 0;
1693 }
1694 if (!emit_expression(bc, src, len, pos)) {
1695 parser_fail(*pos, "ini_get_string expects 4 args");
1696 free(name);
1697 return 0;
1698 }
1699 if (!consume_char(src, len, pos, ')')) {
1700 parser_fail(*pos, "Expected ')' after ini_get_string args");
1701 free(name);
1702 return 0;
1703 }
1705 free(name);
1706 return 1;
1707 }
1708 if (strcmp(name, "ini_get_int") == 0) {
1709 (*pos)++; /* '(' */
1710 if (!emit_expression(bc, src, len, pos)) {
1711 parser_fail(*pos, "ini_get_int expects (handle, section, key, default)");
1712 free(name);
1713 return 0;
1714 }
1715 if (!consume_char(src, len, pos, ',')) {
1716 parser_fail(*pos, "ini_get_int expects 4 args");
1717 free(name);
1718 return 0;
1719 }
1720 if (!emit_expression(bc, src, len, pos)) {
1721 parser_fail(*pos, "ini_get_int expects 4 args");
1722 free(name);
1723 return 0;
1724 }
1725 if (!consume_char(src, len, pos, ',')) {
1726 parser_fail(*pos, "ini_get_int expects 4 args");
1727 free(name);
1728 return 0;
1729 }
1730 if (!emit_expression(bc, src, len, pos)) {
1731 parser_fail(*pos, "ini_get_int expects 4 args");
1732 free(name);
1733 return 0;
1734 }
1735 if (!consume_char(src, len, pos, ',')) {
1736 parser_fail(*pos, "ini_get_int expects 4 args");
1737 free(name);
1738 return 0;
1739 }
1740 if (!emit_expression(bc, src, len, pos)) {
1741 parser_fail(*pos, "ini_get_int expects 4 args");
1742 free(name);
1743 return 0;
1744 }
1745 if (!consume_char(src, len, pos, ')')) {
1746 parser_fail(*pos, "Expected ')' after ini_get_int args");
1747 free(name);
1748 return 0;
1749 }
1751 free(name);
1752 return 1;
1753 }
1754 if (strcmp(name, "ini_get_double") == 0) {
1755 (*pos)++; /* '(' */
1756 if (!emit_expression(bc, src, len, pos)) {
1757 parser_fail(*pos, "ini_get_double expects (handle, section, key, default)");
1758 free(name);
1759 return 0;
1760 }
1761 if (!consume_char(src, len, pos, ',')) {
1762 parser_fail(*pos, "ini_get_double expects 4 args");
1763 free(name);
1764 return 0;
1765 }
1766 if (!emit_expression(bc, src, len, pos)) {
1767 parser_fail(*pos, "ini_get_double expects 4 args");
1768 free(name);
1769 return 0;
1770 }
1771 if (!consume_char(src, len, pos, ',')) {
1772 parser_fail(*pos, "ini_get_double expects 4 args");
1773 free(name);
1774 return 0;
1775 }
1776 if (!emit_expression(bc, src, len, pos)) {
1777 parser_fail(*pos, "ini_get_double expects 4 args");
1778 free(name);
1779 return 0;
1780 }
1781 if (!consume_char(src, len, pos, ',')) {
1782 parser_fail(*pos, "ini_get_double expects 4 args");
1783 free(name);
1784 return 0;
1785 }
1786 if (!emit_expression(bc, src, len, pos)) {
1787 parser_fail(*pos, "ini_get_double expects 4 args");
1788 free(name);
1789 return 0;
1790 }
1791 if (!consume_char(src, len, pos, ')')) {
1792 parser_fail(*pos, "Expected ')' after ini_get_double args");
1793 free(name);
1794 return 0;
1795 }
1797 free(name);
1798 return 1;
1799 }
1800 if (strcmp(name, "ini_get_bool") == 0) {
1801 (*pos)++; /* '(' */
1802 if (!emit_expression(bc, src, len, pos)) {
1803 parser_fail(*pos, "ini_get_bool expects (handle, section, key, default)");
1804 free(name);
1805 return 0;
1806 }
1807 if (!consume_char(src, len, pos, ',')) {
1808 parser_fail(*pos, "ini_get_bool expects 4 args");
1809 free(name);
1810 return 0;
1811 }
1812 if (!emit_expression(bc, src, len, pos)) {
1813 parser_fail(*pos, "ini_get_bool expects 4 args");
1814 free(name);
1815 return 0;
1816 }
1817 if (!consume_char(src, len, pos, ',')) {
1818 parser_fail(*pos, "ini_get_bool expects 4 args");
1819 free(name);
1820 return 0;
1821 }
1822 if (!emit_expression(bc, src, len, pos)) {
1823 parser_fail(*pos, "ini_get_bool expects 4 args");
1824 free(name);
1825 return 0;
1826 }
1827 if (!consume_char(src, len, pos, ',')) {
1828 parser_fail(*pos, "ini_get_bool expects 4 args");
1829 free(name);
1830 return 0;
1831 }
1832 if (!emit_expression(bc, src, len, pos)) {
1833 parser_fail(*pos, "ini_get_bool expects 4 args");
1834 free(name);
1835 return 0;
1836 }
1837 if (!consume_char(src, len, pos, ')')) {
1838 parser_fail(*pos, "Expected ')' after ini_get_bool args");
1839 free(name);
1840 return 0;
1841 }
1843 free(name);
1844 return 1;
1845 }
1846 if (strcmp(name, "ini_set") == 0) {
1847 (*pos)++; /* '(' */
1848 if (!emit_expression(bc, src, len, pos)) {
1849 parser_fail(*pos, "ini_set expects (handle, section, key, value)");
1850 free(name);
1851 return 0;
1852 }
1853 if (!consume_char(src, len, pos, ',')) {
1854 parser_fail(*pos, "ini_set expects 4 args");
1855 free(name);
1856 return 0;
1857 }
1858 if (!emit_expression(bc, src, len, pos)) {
1859 parser_fail(*pos, "ini_set expects 4 args");
1860 free(name);
1861 return 0;
1862 }
1863 if (!consume_char(src, len, pos, ',')) {
1864 parser_fail(*pos, "ini_set expects 4 args");
1865 free(name);
1866 return 0;
1867 }
1868 if (!emit_expression(bc, src, len, pos)) {
1869 parser_fail(*pos, "ini_set expects 4 args");
1870 free(name);
1871 return 0;
1872 }
1873 if (!consume_char(src, len, pos, ',')) {
1874 parser_fail(*pos, "ini_set expects 4 args");
1875 free(name);
1876 return 0;
1877 }
1878 if (!emit_expression(bc, src, len, pos)) {
1879 parser_fail(*pos, "ini_set expects 4 args");
1880 free(name);
1881 return 0;
1882 }
1883 if (!consume_char(src, len, pos, ')')) {
1884 parser_fail(*pos, "Expected ')' after ini_set args");
1885 free(name);
1886 return 0;
1887 }
1889 free(name);
1890 return 1;
1891 }
1892 if (strcmp(name, "ini_unset") == 0) {
1893 (*pos)++; /* '(' */
1894 if (!emit_expression(bc, src, len, pos)) {
1895 parser_fail(*pos, "ini_unset expects (handle, section, key)");
1896 free(name);
1897 return 0;
1898 }
1899 if (!consume_char(src, len, pos, ',')) {
1900 parser_fail(*pos, "ini_unset expects 3 args");
1901 free(name);
1902 return 0;
1903 }
1904 if (!emit_expression(bc, src, len, pos)) {
1905 parser_fail(*pos, "ini_unset expects 3 args");
1906 free(name);
1907 return 0;
1908 }
1909 if (!consume_char(src, len, pos, ',')) {
1910 parser_fail(*pos, "ini_unset expects 3 args");
1911 free(name);
1912 return 0;
1913 }
1914 if (!emit_expression(bc, src, len, pos)) {
1915 parser_fail(*pos, "ini_unset expects 3 args");
1916 free(name);
1917 return 0;
1918 }
1919 if (!consume_char(src, len, pos, ')')) {
1920 parser_fail(*pos, "Expected ')' after ini_unset args");
1921 free(name);
1922 return 0;
1923 }
1925 free(name);
1926 return 1;
1927 }
1928 if (strcmp(name, "ini_save") == 0) {
1929 (*pos)++; /* '(' */
1930 if (!emit_expression(bc, src, len, pos)) {
1931 parser_fail(*pos, "ini_save expects (handle, path)");
1932 free(name);
1933 return 0;
1934 }
1935 if (!consume_char(src, len, pos, ',')) {
1936 parser_fail(*pos, "ini_save expects 2 args");
1937 free(name);
1938 return 0;
1939 }
1940 if (!emit_expression(bc, src, len, pos)) {
1941 parser_fail(*pos, "ini_save expects 2 args");
1942 free(name);
1943 return 0;
1944 }
1945 if (!consume_char(src, len, pos, ')')) {
1946 parser_fail(*pos, "Expected ')' after ini_save args");
1947 free(name);
1948 return 0;
1949 }
1951 free(name);
1952 return 1;
1953 }
1954 /* CURL builtins (minimal interface like JSON) */
1955 if (strcmp(name, "curl_get") == 0) {
1956 (*pos)++; /* '(' */
1957 if (!emit_expression(bc, src, len, pos)) {
1958 parser_fail(*pos, "curl_get expects (url)");
1959 free(name);
1960 return 0;
1961 }
1962 if (!consume_char(src, len, pos, ')')) {
1963 parser_fail(*pos, "Expected ')' after curl_get arg");
1964 free(name);
1965 return 0;
1966 }
1968 free(name);
1969 return 1;
1970 }
1971 /* SQLite builtins */
1972 if (strcmp(name, "sqlite_open") == 0) {
1973 (*pos)++; /* '(' */
1974 if (!emit_expression(bc, src, len, pos)) {
1975 parser_fail(*pos, "sqlite_open expects (path)");
1976 free(name);
1977 return 0;
1978 }
1979 if (!consume_char(src, len, pos, ')')) {
1980 parser_fail(*pos, "Expected ')' after sqlite_open arg");
1981 free(name);
1982 return 0;
1983 }
1985 free(name);
1986 return 1;
1987 }
1988 if (strcmp(name, "sqlite_close") == 0) {
1989 (*pos)++; /* '(' */
1990 if (!emit_expression(bc, src, len, pos)) {
1991 parser_fail(*pos, "sqlite_close expects (handle)");
1992 free(name);
1993 return 0;
1994 }
1995 if (!consume_char(src, len, pos, ')')) {
1996 parser_fail(*pos, "Expected ')' after sqlite_close arg");
1997 free(name);
1998 return 0;
1999 }
2001 free(name);
2002 return 1;
2003 }
2004 if (strcmp(name, "sqlite_exec") == 0) {
2005 (*pos)++; /* '(' */
2006 if (!emit_expression(bc, src, len, pos)) {
2007 parser_fail(*pos, "sqlite_exec expects (handle, sql)");
2008 free(name);
2009 return 0;
2010 }
2011 if (!consume_char(src, len, pos, ',')) {
2012 parser_fail(*pos, "sqlite_exec expects (handle, sql)");
2013 free(name);
2014 return 0;
2015 }
2016 if (!emit_expression(bc, src, len, pos)) {
2017 parser_fail(*pos, "sqlite_exec expects (handle, sql)");
2018 free(name);
2019 return 0;
2020 }
2021 if (!consume_char(src, len, pos, ')')) {
2022 parser_fail(*pos, "Expected ')' after sqlite_exec args");
2023 free(name);
2024 return 0;
2025 }
2027 free(name);
2028 return 1;
2029 }
2030 if (strcmp(name, "sqlite_query") == 0) {
2031 (*pos)++; /* '(' */
2032 if (!emit_expression(bc, src, len, pos)) {
2033 parser_fail(*pos, "sqlite_query expects (handle, sql)");
2034 free(name);
2035 return 0;
2036 }
2037 if (!consume_char(src, len, pos, ',')) {
2038 parser_fail(*pos, "sqlite_query expects (handle, sql)");
2039 free(name);
2040 return 0;
2041 }
2042 if (!emit_expression(bc, src, len, pos)) {
2043 parser_fail(*pos, "sqlite_query expects (handle, sql)");
2044 free(name);
2045 return 0;
2046 }
2047 if (!consume_char(src, len, pos, ')')) {
2048 parser_fail(*pos, "Expected ')' after sqlite_query args");
2049 free(name);
2050 return 0;
2051 }
2053 free(name);
2054 return 1;
2055 }
2056 if (strcmp(name, "curl_post") == 0) {
2057 (*pos)++; /* '(' */
2058 if (!emit_expression(bc, src, len, pos)) {
2059 parser_fail(*pos, "curl_post expects (url, body)");
2060 free(name);
2061 return 0;
2062 }
2063 if (!consume_char(src, len, pos, ',')) {
2064 parser_fail(*pos, "curl_post expects (url, body)");
2065 free(name);
2066 return 0;
2067 }
2068 if (!emit_expression(bc, src, len, pos)) {
2069 parser_fail(*pos, "curl_post expects (url, body)");
2070 free(name);
2071 return 0;
2072 }
2073 if (!consume_char(src, len, pos, ')')) {
2074 parser_fail(*pos, "Expected ')' after curl_post args");
2075 free(name);
2076 return 0;
2077 }
2079 free(name);
2080 return 1;
2081 }
2082 if (strcmp(name, "curl_download") == 0) {
2083 (*pos)++; /* '(' */
2084 if (!emit_expression(bc, src, len, pos)) {
2085 parser_fail(*pos, "curl_download expects (url, path)");
2086 free(name);
2087 return 0;
2088 }
2089 if (!consume_char(src, len, pos, ',')) {
2090 parser_fail(*pos, "curl_download expects (url, path)");
2091 free(name);
2092 return 0;
2093 }
2094 if (!emit_expression(bc, src, len, pos)) {
2095 parser_fail(*pos, "curl_download expects (url, path)");
2096 free(name);
2097 return 0;
2098 }
2099 if (!consume_char(src, len, pos, ')')) {
2100 parser_fail(*pos, "Expected ')' after curl_download args");
2101 free(name);
2102 return 0;
2103 }
2105 free(name);
2106 return 1;
2107 }
2108 /* OpenSSL (md5/sha256/sha512) */
2109 if (strcmp(name, "openssl_md5") == 0) {
2110 (*pos)++; /* '(' */
2111 if (!emit_expression(bc, src, len, pos)) {
2112 parser_fail(*pos, "openssl_md5 expects (data)");
2113 free(name);
2114 return 0;
2115 }
2116 if (!consume_char(src, len, pos, ')')) {
2117 parser_fail(*pos, "Expected ')' after openssl_md5 arg");
2118 free(name);
2119 return 0;
2120 }
2122 free(name);
2123 return 1;
2124 }
2125 if (strcmp(name, "openssl_sha256") == 0) {
2126 (*pos)++; /* '(' */
2127 if (!emit_expression(bc, src, len, pos)) {
2128 parser_fail(*pos, "openssl_sha256 expects (data)");
2129 free(name);
2130 return 0;
2131 }
2132 if (!consume_char(src, len, pos, ')')) {
2133 parser_fail(*pos, "Expected ')' after openssl_sha256 arg");
2134 free(name);
2135 return 0;
2136 }
2138 free(name);
2139 return 1;
2140 }
2141 if (strcmp(name, "openssl_sha512") == 0) {
2142 (*pos)++; /* '(' */
2143 if (!emit_expression(bc, src, len, pos)) {
2144 parser_fail(*pos, "openssl_sha512 expects (data)");
2145 free(name);
2146 return 0;
2147 }
2148 if (!consume_char(src, len, pos, ')')) {
2149 parser_fail(*pos, "Expected ')' after openssl_sha512 arg");
2150 free(name);
2151 return 0;
2152 }
2154 free(name);
2155 return 1;
2156 }
2157 if (strcmp(name, "openssl_ripemd160") == 0) {
2158 (*pos)++; /* '(' */
2159 if (!emit_expression(bc, src, len, pos)) {
2160 parser_fail(*pos, "openssl_ripemd160 expects (data)");
2161 free(name);
2162 return 0;
2163 }
2164 if (!consume_char(src, len, pos, ')')) {
2165 parser_fail(*pos, "Expected ')' after openssl_ripemd160 arg");
2166 free(name);
2167 return 0;
2168 }
2170 free(name);
2171 return 1;
2172 }
2173 /* LibreSSL builtins removed */
2174 /* PCSC builtins */
2175 if (strcmp(name, "pcsc_establish") == 0) {
2176 (*pos)++; /* '(' */
2177 /* no args */
2178 if (!consume_char(src, len, pos, ')')) {
2179 parser_fail(*pos, "pcsc_establish expects ()");
2180 free(name);
2181 return 0;
2182 }
2184 free(name);
2185 return 1;
2186 }
2187 /* PCRE2 builtins */
2188 if (strcmp(name, "pcre2_test") == 0) {
2189 (*pos)++; /* '(' */
2190 /* (pattern, text, flags) */
2191 if (!emit_expression(bc, src, len, pos)) {
2192 parser_fail(*pos, "pcre2_test expects (pattern, text, flags)");
2193 free(name);
2194 return 0;
2195 }
2196 if (!consume_char(src, len, pos, ',')) {
2197 parser_fail(*pos, "pcre2_test expects (pattern, text, flags)");
2198 free(name);
2199 return 0;
2200 }
2201 if (!emit_expression(bc, src, len, pos)) {
2202 parser_fail(*pos, "pcre2_test expects (pattern, text, flags)");
2203 free(name);
2204 return 0;
2205 }
2206 if (!consume_char(src, len, pos, ',')) {
2207 parser_fail(*pos, "pcre2_test expects (pattern, text, flags)");
2208 free(name);
2209 return 0;
2210 }
2211 if (!emit_expression(bc, src, len, pos)) {
2212 parser_fail(*pos, "pcre2_test expects (pattern, text, flags)");
2213 free(name);
2214 return 0;
2215 }
2216 if (!consume_char(src, len, pos, ')')) {
2217 parser_fail(*pos, "Expected ')' after pcre2_test args");
2218 free(name);
2219 return 0;
2220 }
2222 free(name);
2223 return 1;
2224 }
2225 if (strcmp(name, "pcre2_match") == 0) {
2226 (*pos)++; /* '(' */
2227 if (!emit_expression(bc, src, len, pos)) {
2228 parser_fail(*pos, "pcre2_match expects (pattern, text, flags)");
2229 free(name);
2230 return 0;
2231 }
2232 if (!consume_char(src, len, pos, ',')) {
2233 parser_fail(*pos, "pcre2_match expects (pattern, text, flags)");
2234 free(name);
2235 return 0;
2236 }
2237 if (!emit_expression(bc, src, len, pos)) {
2238 parser_fail(*pos, "pcre2_match expects (pattern, text, flags)");
2239 free(name);
2240 return 0;
2241 }
2242 if (!consume_char(src, len, pos, ',')) {
2243 parser_fail(*pos, "pcre2_match expects (pattern, text, flags)");
2244 free(name);
2245 return 0;
2246 }
2247 if (!emit_expression(bc, src, len, pos)) {
2248 parser_fail(*pos, "pcre2_match expects (pattern, text, flags)");
2249 free(name);
2250 return 0;
2251 }
2252 if (!consume_char(src, len, pos, ')')) {
2253 parser_fail(*pos, "Expected ')' after pcre2_match args");
2254 free(name);
2255 return 0;
2256 }
2258 free(name);
2259 return 1;
2260 }
2261 if (strcmp(name, "pcre2_findall") == 0) {
2262 (*pos)++; /* '(' */
2263 if (!emit_expression(bc, src, len, pos)) {
2264 parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)");
2265 free(name);
2266 return 0;
2267 }
2268 if (!consume_char(src, len, pos, ',')) {
2269 parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)");
2270 free(name);
2271 return 0;
2272 }
2273 if (!emit_expression(bc, src, len, pos)) {
2274 parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)");
2275 free(name);
2276 return 0;
2277 }
2278 if (!consume_char(src, len, pos, ',')) {
2279 parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)");
2280 free(name);
2281 return 0;
2282 }
2283 if (!emit_expression(bc, src, len, pos)) {
2284 parser_fail(*pos, "pcre2_findall expects (pattern, text, flags)");
2285 free(name);
2286 return 0;
2287 }
2288 if (!consume_char(src, len, pos, ')')) {
2289 parser_fail(*pos, "Expected ')' after pcre2_findall args");
2290 free(name);
2291 return 0;
2292 }
2294 free(name);
2295 return 1;
2296 }
2297 /* PCSC builtins (optional) */
2298 if (strcmp(name, "pcsc_release") == 0) {
2299 (*pos)++; /* '(' */
2300 if (!emit_expression(bc, src, len, pos)) {
2301 parser_fail(*pos, "pcsc_release expects 1 argument (ctx)");
2302 free(name);
2303 return 0;
2304 }
2305 if (!consume_char(src, len, pos, ')')) {
2306 parser_fail(*pos, "Expected ')' after pcsc_release arg");
2307 free(name);
2308 return 0;
2309 }
2311 free(name);
2312 return 1;
2313 }
2314 if (strcmp(name, "pcsc_list_readers") == 0) {
2315 (*pos)++; /* '(' */
2316 if (!emit_expression(bc, src, len, pos)) {
2317 parser_fail(*pos, "pcsc_list_readers expects 1 argument (ctx)");
2318 free(name);
2319 return 0;
2320 }
2321 if (!consume_char(src, len, pos, ')')) {
2322 parser_fail(*pos, "Expected ')' after pcsc_list_readers arg");
2323 free(name);
2324 return 0;
2325 }
2327 free(name);
2328 return 1;
2329 }
2330 if (strcmp(name, "pcsc_connect") == 0) {
2331 (*pos)++; /* '(' */
2332 /* ctx, reader */
2333 if (!emit_expression(bc, src, len, pos)) {
2334 parser_fail(*pos, "pcsc_connect expects (ctx, reader)");
2335 free(name);
2336 return 0;
2337 }
2338 if (!consume_char(src, len, pos, ',')) {
2339 parser_fail(*pos, "pcsc_connect expects (ctx, reader)");
2340 free(name);
2341 return 0;
2342 }
2343 if (!emit_expression(bc, src, len, pos)) {
2344 parser_fail(*pos, "pcsc_connect expects (ctx, reader)");
2345 free(name);
2346 return 0;
2347 }
2348 if (!consume_char(src, len, pos, ')')) {
2349 parser_fail(*pos, "Expected ')' after pcsc_connect args");
2350 free(name);
2351 return 0;
2352 }
2354 free(name);
2355 return 1;
2356 }
2357 if (strcmp(name, "pcsc_disconnect") == 0) {
2358 (*pos)++; /* '(' */
2359 if (!emit_expression(bc, src, len, pos)) {
2360 parser_fail(*pos, "pcsc_disconnect expects 1 argument (handle)");
2361 free(name);
2362 return 0;
2363 }
2364 if (!consume_char(src, len, pos, ')')) {
2365 parser_fail(*pos, "Expected ')' after pcsc_disconnect arg");
2366 free(name);
2367 return 0;
2368 }
2370 free(name);
2371 return 1;
2372 }
2373 if (strcmp(name, "pcsc_transmit") == 0) {
2374 (*pos)++; /* '(' */
2375 /* handle, bytes */
2376 if (!emit_expression(bc, src, len, pos)) {
2377 parser_fail(*pos, "pcsc_transmit expects (handle, bytes)");
2378 free(name);
2379 return 0;
2380 }
2381 if (!consume_char(src, len, pos, ',')) {
2382 parser_fail(*pos, "pcsc_transmit expects (handle, bytes)");
2383 free(name);
2384 return 0;
2385 }
2386 if (!emit_expression(bc, src, len, pos)) {
2387 parser_fail(*pos, "pcsc_transmit expects (handle, bytes)");
2388 free(name);
2389 return 0;
2390 }
2391 if (!consume_char(src, len, pos, ')')) {
2392 parser_fail(*pos, "Expected ')' after pcsc_transmit args");
2393 free(name);
2394 return 0;
2395 }
2397 free(name);
2398 return 1;
2399 }
2400 /* Socket builtins */
2401 if (strcmp(name, "tcp_listen") == 0) {
2402 (*pos)++; /* '(' */
2403 if (!emit_expression(bc, src, len, pos)) {
2404 parser_fail(*pos, "tcp_listen expects (port, backlog)");
2405 free(name);
2406 return 0;
2407 }
2408 if (!consume_char(src, len, pos, ',')) {
2409 parser_fail(*pos, "tcp_listen expects (port, backlog)");
2410 free(name);
2411 return 0;
2412 }
2413 if (!emit_expression(bc, src, len, pos)) {
2414 parser_fail(*pos, "tcp_listen expects (port, backlog)");
2415 free(name);
2416 return 0;
2417 }
2418 if (!consume_char(src, len, pos, ')')) {
2419 parser_fail(*pos, "Expected ')' after tcp_listen args");
2420 free(name);
2421 return 0;
2422 }
2424 free(name);
2425 return 1;
2426 }
2427 if (strcmp(name, "tcp_accept") == 0) {
2428 (*pos)++; /* '(' */
2429 if (!emit_expression(bc, src, len, pos)) {
2430 parser_fail(*pos, "tcp_accept expects (listen_fd)");
2431 free(name);
2432 return 0;
2433 }
2434 if (!consume_char(src, len, pos, ')')) {
2435 parser_fail(*pos, "Expected ')' after tcp_accept arg");
2436 free(name);
2437 return 0;
2438 }
2440 free(name);
2441 return 1;
2442 }
2443 if (strcmp(name, "tcp_connect") == 0) {
2444 (*pos)++; /* '(' */
2445 if (!emit_expression(bc, src, len, pos)) {
2446 parser_fail(*pos, "tcp_connect expects (host, port)");
2447 free(name);
2448 return 0;
2449 }
2450 if (!consume_char(src, len, pos, ',')) {
2451 parser_fail(*pos, "tcp_connect expects (host, port)");
2452 free(name);
2453 return 0;
2454 }
2455 if (!emit_expression(bc, src, len, pos)) {
2456 parser_fail(*pos, "tcp_connect expects (host, port)");
2457 free(name);
2458 return 0;
2459 }
2460 if (!consume_char(src, len, pos, ')')) {
2461 parser_fail(*pos, "Expected ')' after tcp_connect args");
2462 free(name);
2463 return 0;
2464 }
2466 free(name);
2467 return 1;
2468 }
2469 if (strcmp(name, "sock_send") == 0) {
2470 (*pos)++; /* '(' */
2471 if (!emit_expression(bc, src, len, pos)) {
2472 parser_fail(*pos, "sock_send expects (fd, data)");
2473 free(name);
2474 return 0;
2475 }
2476 if (!consume_char(src, len, pos, ',')) {
2477 parser_fail(*pos, "sock_send expects (fd, data)");
2478 free(name);
2479 return 0;
2480 }
2481 if (!emit_expression(bc, src, len, pos)) {
2482 parser_fail(*pos, "sock_send expects (fd, data)");
2483 free(name);
2484 return 0;
2485 }
2486 if (!consume_char(src, len, pos, ')')) {
2487 parser_fail(*pos, "Expected ')' after sock_send args");
2488 free(name);
2489 return 0;
2490 }
2492 free(name);
2493 return 1;
2494 }
2495 if (strcmp(name, "sock_recv") == 0) {
2496 (*pos)++; /* '(' */
2497 if (!emit_expression(bc, src, len, pos)) {
2498 parser_fail(*pos, "sock_recv expects (fd, maxlen)");
2499 free(name);
2500 return 0;
2501 }
2502 if (!consume_char(src, len, pos, ',')) {
2503 parser_fail(*pos, "sock_recv expects (fd, maxlen)");
2504 free(name);
2505 return 0;
2506 }
2507 if (!emit_expression(bc, src, len, pos)) {
2508 parser_fail(*pos, "sock_recv expects (fd, maxlen)");
2509 free(name);
2510 return 0;
2511 }
2512 if (!consume_char(src, len, pos, ')')) {
2513 parser_fail(*pos, "Expected ')' after sock_recv args");
2514 free(name);
2515 return 0;
2516 }
2518 free(name);
2519 return 1;
2520 }
2521 if (strcmp(name, "sock_close") == 0) {
2522 (*pos)++; /* '(' */
2523 if (!emit_expression(bc, src, len, pos)) {
2524 parser_fail(*pos, "sock_close expects (fd)");
2525 free(name);
2526 return 0;
2527 }
2528 if (!consume_char(src, len, pos, ')')) {
2529 parser_fail(*pos, "Expected ')' after sock_close arg");
2530 free(name);
2531 return 0;
2532 }
2534 free(name);
2535 return 1;
2536 }
2537 if (strcmp(name, "unix_listen") == 0) {
2538 (*pos)++; /* '(' */
2539 if (!emit_expression(bc, src, len, pos)) {
2540 parser_fail(*pos, "unix_listen expects (path, backlog)");
2541 free(name);
2542 return 0;
2543 }
2544 if (!consume_char(src, len, pos, ',')) {
2545 parser_fail(*pos, "unix_listen expects (path, backlog)");
2546 free(name);
2547 return 0;
2548 }
2549 if (!emit_expression(bc, src, len, pos)) {
2550 parser_fail(*pos, "unix_listen expects (path, backlog)");
2551 free(name);
2552 return 0;
2553 }
2554 if (!consume_char(src, len, pos, ')')) {
2555 parser_fail(*pos, "Expected ')' after unix_listen args");
2556 free(name);
2557 return 0;
2558 }
2560 free(name);
2561 return 1;
2562 }
2563 if (strcmp(name, "unix_connect") == 0) {
2564 (*pos)++; /* '(' */
2565 if (!emit_expression(bc, src, len, pos)) {
2566 parser_fail(*pos, "unix_connect expects (path)");
2567 free(name);
2568 return 0;
2569 }
2570 if (!consume_char(src, len, pos, ')')) {
2571 parser_fail(*pos, "Expected ')' after unix_connect arg");
2572 free(name);
2573 return 0;
2574 }
2576 free(name);
2577 return 1;
2578 }
2579 /* Async-friendly FD helpers */
2580 if (strcmp(name, "fd_set_nonblock") == 0) {
2581 (*pos)++; /* '(' */
2582 /* Expect (fd, on) -> push fd then on so VM pops on first */
2583 if (!emit_expression(bc, src, len, pos)) {
2584 parser_fail(*pos, "fd_set_nonblock expects (fd, on)");
2585 free(name);
2586 return 0;
2587 }
2588 if (!consume_char(src, len, pos, ',')) {
2589 parser_fail(*pos, "fd_set_nonblock expects (fd, on)");
2590 free(name);
2591 return 0;
2592 }
2593 if (!emit_expression(bc, src, len, pos)) {
2594 parser_fail(*pos, "fd_set_nonblock expects (fd, on)");
2595 free(name);
2596 return 0;
2597 }
2598 if (!consume_char(src, len, pos, ')')) {
2599 parser_fail(*pos, "Expected ')' after fd_set_nonblock args");
2600 free(name);
2601 return 0;
2602 }
2604 free(name);
2605 return 1;
2606 }
2607 if (strcmp(name, "fd_poll_read") == 0) {
2608 (*pos)++; /* '(' */
2609 /* Expect (fd, timeout_ms) -> push fd then timeout so VM pops timeout first */
2610 if (!emit_expression(bc, src, len, pos)) {
2611 parser_fail(*pos, "fd_poll_read expects (fd, timeout_ms)");
2612 free(name);
2613 return 0;
2614 }
2615 if (!consume_char(src, len, pos, ',')) {
2616 parser_fail(*pos, "fd_poll_read expects (fd, timeout_ms)");
2617 free(name);
2618 return 0;
2619 }
2620 if (!emit_expression(bc, src, len, pos)) {
2621 parser_fail(*pos, "fd_poll_read expects (fd, timeout_ms)");
2622 free(name);
2623 return 0;
2624 }
2625 if (!consume_char(src, len, pos, ')')) {
2626 parser_fail(*pos, "Expected ')' after fd_poll_read args");
2627 free(name);
2628 return 0;
2629 }
2631 free(name);
2632 return 1;
2633 }
2634 if (strcmp(name, "fd_poll_write") == 0) {
2635 (*pos)++; /* '(' */
2636 /* Expect (fd, timeout_ms) */
2637 if (!emit_expression(bc, src, len, pos)) {
2638 parser_fail(*pos, "fd_poll_write expects (fd, timeout_ms)");
2639 free(name);
2640 return 0;
2641 }
2642 if (!consume_char(src, len, pos, ',')) {
2643 parser_fail(*pos, "fd_poll_write expects (fd, timeout_ms)");
2644 free(name);
2645 return 0;
2646 }
2647 if (!emit_expression(bc, src, len, pos)) {
2648 parser_fail(*pos, "fd_poll_write expects (fd, timeout_ms)");
2649 free(name);
2650 return 0;
2651 }
2652 if (!consume_char(src, len, pos, ')')) {
2653 parser_fail(*pos, "Expected ')' after fd_poll_write args");
2654 free(name);
2655 return 0;
2656 }
2658 free(name);
2659 return 1;
2660 }
2661 /* Serial builtins */
2662 if (strcmp(name, "serial_open") == 0) {
2663 (*pos)++; /* '(' */
2664 if (!emit_expression(bc, src, len, pos)) {
2665 parser_fail(*pos, "serial_open expects (path, baud)");
2666 free(name);
2667 return 0;
2668 }
2669 if (!consume_char(src, len, pos, ',')) {
2670 parser_fail(*pos, "serial_open expects (path, baud)");
2671 free(name);
2672 return 0;
2673 }
2674 if (!emit_expression(bc, src, len, pos)) {
2675 parser_fail(*pos, "serial_open expects (path, baud)");
2676 free(name);
2677 return 0;
2678 }
2679 if (!consume_char(src, len, pos, ')')) {
2680 parser_fail(*pos, "Expected ')' after serial_open args");
2681 free(name);
2682 return 0;
2683 }
2685 free(name);
2686 return 1;
2687 }
2688 if (strcmp(name, "serial_config") == 0) {
2689 (*pos)++; /* '(' */
2690 // fd, data_bits, parity, stop_bits, flow_control
2691 for (int i = 0; i < 5; ++i) {
2692 if (!emit_expression(bc, src, len, pos)) {
2693 parser_fail(*pos, "serial_config expects 5 arguments");
2694 free(name);
2695 return 0;
2696 }
2697 if (i < 4) {
2698 if (!consume_char(src, len, pos, ',')) {
2699 parser_fail(*pos, "serial_config expects 5 arguments");
2700 free(name);
2701 return 0;
2702 }
2703 }
2704 }
2705 if (!consume_char(src, len, pos, ')')) {
2706 parser_fail(*pos, "Expected ')' after serial_config args");
2707 free(name);
2708 return 0;
2709 }
2711 free(name);
2712 return 1;
2713 }
2714 if (strcmp(name, "serial_send") == 0) {
2715 (*pos)++; /* '(' */
2716 if (!emit_expression(bc, src, len, pos)) {
2717 parser_fail(*pos, "serial_send expects (fd, data)");
2718 free(name);
2719 return 0;
2720 }
2721 if (!consume_char(src, len, pos, ',')) {
2722 parser_fail(*pos, "serial_send expects (fd, data)");
2723 free(name);
2724 return 0;
2725 }
2726 if (!emit_expression(bc, src, len, pos)) {
2727 parser_fail(*pos, "serial_send expects (fd, data)");
2728 free(name);
2729 return 0;
2730 }
2731 if (!consume_char(src, len, pos, ')')) {
2732 parser_fail(*pos, "Expected ')' after serial_send args");
2733 free(name);
2734 return 0;
2735 }
2737 free(name);
2738 return 1;
2739 }
2740 if (strcmp(name, "serial_recv") == 0) {
2741 (*pos)++; /* '(' */
2742 if (!emit_expression(bc, src, len, pos)) {
2743 parser_fail(*pos, "serial_recv expects (fd, maxlen)");
2744 free(name);
2745 return 0;
2746 }
2747 if (!consume_char(src, len, pos, ',')) {
2748 parser_fail(*pos, "serial_recv expects (fd, maxlen)");
2749 free(name);
2750 return 0;
2751 }
2752 if (!emit_expression(bc, src, len, pos)) {
2753 parser_fail(*pos, "serial_recv expects (fd, maxlen)");
2754 free(name);
2755 return 0;
2756 }
2757 if (!consume_char(src, len, pos, ')')) {
2758 parser_fail(*pos, "Expected ')' after serial_recv args");
2759 free(name);
2760 return 0;
2761 }
2763 free(name);
2764 return 1;
2765 }
2766 if (strcmp(name, "serial_close") == 0) {
2767 (*pos)++; /* '(' */
2768 if (!emit_expression(bc, src, len, pos)) {
2769 parser_fail(*pos, "serial_close expects (fd)");
2770 free(name);
2771 return 0;
2772 }
2773 if (!consume_char(src, len, pos, ')')) {
2774 parser_fail(*pos, "Expected ')' after serial_close arg");
2775 free(name);
2776 return 0;
2777 }
2779 free(name);
2780 return 1;
2781 }
2782 /* string ops */
2783 if (strcmp(name, "split") == 0) {
2784 (*pos)++; /* '(' */
2785 if (!emit_expression(bc, src, len, pos)) {
2786 parser_fail(*pos, "split expects string");
2787 free(name);
2788 return 0;
2789 }
2790 if (*pos < len && src[*pos] == ',') {
2791 (*pos)++;
2792 skip_spaces(src, len, pos);
2793 } else {
2794 parser_fail(*pos, "split expects 2 args");
2795 free(name);
2796 return 0;
2797 }
2798 if (!emit_expression(bc, src, len, pos)) {
2799 parser_fail(*pos, "split expects separator");
2800 free(name);
2801 return 0;
2802 }
2803 if (!consume_char(src, len, pos, ')')) {
2804 parser_fail(*pos, "Expected ')' after split args");
2805 free(name);
2806 return 0;
2807 }
2809 free(name);
2810 return 1;
2811 }
2812 if (strcmp(name, "join") == 0) {
2813 (*pos)++; /* '(' */
2814 if (!emit_expression(bc, src, len, pos)) {
2815 parser_fail(*pos, "join expects array");
2816 free(name);
2817 return 0;
2818 }
2819 if (*pos < len && src[*pos] == ',') {
2820 (*pos)++;
2821 skip_spaces(src, len, pos);
2822 } else {
2823 parser_fail(*pos, "join expects 2 args");
2824 free(name);
2825 return 0;
2826 }
2827 if (!emit_expression(bc, src, len, pos)) {
2828 parser_fail(*pos, "join expects separator");
2829 free(name);
2830 return 0;
2831 }
2832 if (!consume_char(src, len, pos, ')')) {
2833 parser_fail(*pos, "Expected ')' after join args");
2834 free(name);
2835 return 0;
2836 }
2838 free(name);
2839 return 1;
2840 }
2841 if (strcmp(name, "substr") == 0) {
2842 (*pos)++; /* '(' */
2843 if (!emit_expression(bc, src, len, pos)) {
2844 parser_fail(*pos, "substr expects string");
2845 free(name);
2846 return 0;
2847 }
2848 if (*pos < len && src[*pos] == ',') {
2849 (*pos)++;
2850 skip_spaces(src, len, pos);
2851 } else {
2852 parser_fail(*pos, "substr expects 3 args");
2853 free(name);
2854 return 0;
2855 }
2856 if (!emit_expression(bc, src, len, pos)) {
2857 parser_fail(*pos, "substr expects start");
2858 free(name);
2859 return 0;
2860 }
2861 if (*pos < len && src[*pos] == ',') {
2862 (*pos)++;
2863 skip_spaces(src, len, pos);
2864 } else {
2865 parser_fail(*pos, "substr expects 3 args");
2866 free(name);
2867 return 0;
2868 }
2869 if (!emit_expression(bc, src, len, pos)) {
2870 parser_fail(*pos, "substr expects len");
2871 free(name);
2872 return 0;
2873 }
2874 if (!consume_char(src, len, pos, ')')) {
2875 parser_fail(*pos, "Expected ')' after substr args");
2876 free(name);
2877 return 0;
2878 }
2880 free(name);
2881 return 1;
2882 }
2883 if (strcmp(name, "find") == 0) {
2884 (*pos)++; /* '(' */
2885 if (!emit_expression(bc, src, len, pos)) {
2886 parser_fail(*pos, "find expects haystack");
2887 free(name);
2888 return 0;
2889 }
2890 if (*pos < len && src[*pos] == ',') {
2891 (*pos)++;
2892 skip_spaces(src, len, pos);
2893 } else {
2894 parser_fail(*pos, "find expects 2 args");
2895 free(name);
2896 return 0;
2897 }
2898 if (!emit_expression(bc, src, len, pos)) {
2899 parser_fail(*pos, "find expects needle");
2900 free(name);
2901 return 0;
2902 }
2903 if (!consume_char(src, len, pos, ')')) {
2904 parser_fail(*pos, "Expected ')' after find args");
2905 free(name);
2906 return 0;
2907 }
2909 free(name);
2910 return 1;
2911 }
2912 /* regex ops */
2913 if (strcmp(name, "regex_match") == 0) {
2914 (*pos)++; /* '(' */
2915 if (!emit_expression(bc, src, len, pos)) {
2916 parser_fail(*pos, "regex_match expects text");
2917 free(name);
2918 return 0;
2919 }
2920 if (*pos < len && src[*pos] == ',') {
2921 (*pos)++;
2922 skip_spaces(src, len, pos);
2923 } else {
2924 parser_fail(*pos, "regex_match expects 2 args");
2925 free(name);
2926 return 0;
2927 }
2928 if (!emit_expression(bc, src, len, pos)) {
2929 parser_fail(*pos, "regex_match expects pattern");
2930 free(name);
2931 return 0;
2932 }
2933 if (!consume_char(src, len, pos, ')')) {
2934 parser_fail(*pos, "Expected ')' after regex_match args");
2935 free(name);
2936 return 0;
2937 }
2939 free(name);
2940 return 1;
2941 }
2942 if (strcmp(name, "regex_search") == 0) {
2943 (*pos)++; /* '(' */
2944 if (!emit_expression(bc, src, len, pos)) {
2945 parser_fail(*pos, "regex_search expects text");
2946 free(name);
2947 return 0;
2948 }
2949 if (*pos < len && src[*pos] == ',') {
2950 (*pos)++;
2951 skip_spaces(src, len, pos);
2952 } else {
2953 parser_fail(*pos, "regex_search expects 2 args");
2954 free(name);
2955 return 0;
2956 }
2957 if (!emit_expression(bc, src, len, pos)) {
2958 parser_fail(*pos, "regex_search expects pattern");
2959 free(name);
2960 return 0;
2961 }
2962 if (!consume_char(src, len, pos, ')')) {
2963 parser_fail(*pos, "Expected ')' after regex_search args");
2964 free(name);
2965 return 0;
2966 }
2968 free(name);
2969 return 1;
2970 }
2971 if (strcmp(name, "regex_replace") == 0) {
2972 (*pos)++; /* '(' */
2973 if (!emit_expression(bc, src, len, pos)) {
2974 parser_fail(*pos, "regex_replace expects text");
2975 free(name);
2976 return 0;
2977 }
2978 if (*pos < len && src[*pos] == ',') {
2979 (*pos)++;
2980 skip_spaces(src, len, pos);
2981 } else {
2982 parser_fail(*pos, "regex_replace expects 3 args");
2983 free(name);
2984 return 0;
2985 }
2986 if (!emit_expression(bc, src, len, pos)) {
2987 parser_fail(*pos, "regex_replace expects pattern");
2988 free(name);
2989 return 0;
2990 }
2991 if (*pos < len && src[*pos] == ',') {
2992 (*pos)++;
2993 skip_spaces(src, len, pos);
2994 } else {
2995 parser_fail(*pos, "regex_replace expects 3 args");
2996 free(name);
2997 return 0;
2998 }
2999 if (!emit_expression(bc, src, len, pos)) {
3000 parser_fail(*pos, "regex_replace expects replacement");
3001 free(name);
3002 return 0;
3003 }
3004 if (!consume_char(src, len, pos, ')')) {
3005 parser_fail(*pos, "Expected ')' after regex_replace args");
3006 free(name);
3007 return 0;
3008 }
3010 free(name);
3011 return 1;
3012 }
3013 /* array utils */
3014 if (strcmp(name, "contains") == 0) {
3015 (*pos)++; /* '(' */
3016 if (!emit_expression(bc, src, len, pos)) {
3017 parser_fail(*pos, "contains expects array");
3018 free(name);
3019 return 0;
3020 }
3021 if (*pos < len && src[*pos] == ',') {
3022 (*pos)++;
3023 skip_spaces(src, len, pos);
3024 } else {
3025 parser_fail(*pos, "contains expects 2 args");
3026 free(name);
3027 return 0;
3028 }
3029 if (!emit_expression(bc, src, len, pos)) {
3030 parser_fail(*pos, "contains expects value");
3031 free(name);
3032 return 0;
3033 }
3034 if (!consume_char(src, len, pos, ')')) {
3035 parser_fail(*pos, "Expected ')' after contains args");
3036 free(name);
3037 return 0;
3038 }
3040 free(name);
3041 return 1;
3042 }
3043 if (strcmp(name, "indexOf") == 0) {
3044 (*pos)++; /* '(' */
3045 if (!emit_expression(bc, src, len, pos)) {
3046 parser_fail(*pos, "indexOf expects array");
3047 free(name);
3048 return 0;
3049 }
3050 if (*pos < len && src[*pos] == ',') {
3051 (*pos)++;
3052 skip_spaces(src, len, pos);
3053 } else {
3054 parser_fail(*pos, "indexOf expects 2 args");
3055 free(name);
3056 return 0;
3057 }
3058 if (!emit_expression(bc, src, len, pos)) {
3059 parser_fail(*pos, "indexOf expects value");
3060 free(name);
3061 return 0;
3062 }
3063 if (!consume_char(src, len, pos, ')')) {
3064 parser_fail(*pos, "Expected ')' after indexOf args");
3065 free(name);
3066 return 0;
3067 }
3069 free(name);
3070 return 1;
3071 }
3072 if (strcmp(name, "clear") == 0) {
3073 (*pos)++; /* '(' */
3074 if (!emit_expression(bc, src, len, pos)) {
3075 parser_fail(*pos, "clear expects array");
3076 free(name);
3077 return 0;
3078 }
3079 if (!consume_char(src, len, pos, ')')) {
3080 parser_fail(*pos, "Expected ')' after clear arg");
3081 free(name);
3082 return 0;
3083 }
3085 free(name);
3086 return 1;
3087 }
3088 /* iteration helpers */
3089 if (strcmp(name, "enumerate") == 0) {
3090 (*pos)++; /* '(' */
3091 if (!emit_expression(bc, src, len, pos)) {
3092 parser_fail(*pos, "enumerate expects array");
3093 free(name);
3094 return 0;
3095 }
3096 if (!consume_char(src, len, pos, ')')) {
3097 parser_fail(*pos, "Expected ')' after enumerate arg");
3098 free(name);
3099 return 0;
3100 }
3102 free(name);
3103 return 1;
3104 }
3105 if (strcmp(name, "map") == 0) {
3106 (*pos)++; /* '(' */
3107 /* arr */
3108 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3109 parser_fail(*pos, "map expects (array, function)");
3110 free(name);
3111 return 0;
3112 }
3113 /* store arr -> __map_arr */
3114 char tarr[64];
3115 snprintf(tarr, sizeof(tarr), "__map_arr_%d", g_temp_counter++);
3116 int larr = -1, garr = -1;
3117 if (g_locals) {
3118 larr = local_add(tarr);
3120 } else {
3121 garr = sym_index(tarr);
3123 }
3124 /* func */
3125 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3126 parser_fail(*pos, "map expects (array, function)");
3127 free(name);
3128 return 0;
3129 }
3130 char tfn[64];
3131 snprintf(tfn, sizeof(tfn), "__map_fn_%d", g_temp_counter++);
3132 int lfn = -1, gfn = -1;
3133 if (g_locals) {
3134 lfn = local_add(tfn);
3136 } else {
3137 gfn = sym_index(tfn);
3139 }
3140 /* res array */
3142 char tres[64];
3143 snprintf(tres, sizeof(tres), "__map_res_%d", g_temp_counter++);
3144 int lres = -1, gres = -1;
3145 if (g_locals) {
3146 lres = local_add(tres);
3148 } else {
3149 gres = sym_index(tres);
3151 }
3152 /* i=0 */
3153 int c0 = bytecode_add_constant(bc, make_int(0));
3155 char ti[64];
3156 snprintf(ti, sizeof(ti), "__map_i_%d", g_temp_counter++);
3157 int li = -1, gi = -1;
3158 if (g_locals) {
3159 li = local_add(ti);
3161 } else {
3162 gi = sym_index(ti);
3164 }
3165 /* loop start */
3166 int loop_start = bc->instr_count;
3167 /* i < len(arr) */
3168 if (g_locals) {
3171 } else {
3174 }
3178 /* elem = arr[i] */
3179 if (g_locals) {
3182 } else {
3185 }
3187 /* call fn(elem) */
3188 if (g_locals) {
3190 } else {
3192 }
3193 /* reorder: we need fn below arg -> push fn first then arg already on stack? We currently have elem on stack; push fn now results top=fn. We want top args then function; OP_CALL expects fn then pops args? Our OP_CALL pops function after args; earlier compile of calls: they push function then args then OP_CALL. So we need to swap: push fn, then swap to make function below arg */
3196 /* Append to result via indexed assignment: res[len(res)] = value */
3197 /* Store computed value to a temp */
3198 char tv[64];
3199 snprintf(tv, sizeof(tv), "__map_v_%d", g_temp_counter++);
3200 int lv = -1, gv = -1;
3201 if (g_locals) {
3202 lv = local_add(tv);
3204 } else {
3205 gv = sym_index(tv);
3207 }
3208
3209 /* Push array (for INDEX_SET we need stack: value, index, array; we will build array, index, then value) */
3210 if (g_locals) {
3212 } else {
3214 }
3215
3216 /* Compute index = len(res) */
3217 if (g_locals) {
3219 } else {
3221 }
3223
3224 /* Load value back on top */
3225 if (g_locals) {
3227 } else {
3229 }
3230
3231 /* Append via insert: res.insert(index=len(res), value) */
3233 /* dApache-2.0ard returned new length */
3235
3236 /* i++ */
3237 int c1 = bytecode_add_constant(bc, make_int(1));
3238 if (g_locals) {
3243 } else {
3248 }
3249 bytecode_add_instruction(bc, OP_JUMP, loop_start);
3251 /* result value on stack */
3252 if (g_locals) {
3254 } else {
3256 }
3257 free(name);
3258 return 1;
3259 }
3260 if (strcmp(name, "filter") == 0) {
3261 (*pos)++; /* '(' */
3262 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3263 parser_fail(*pos, "filter expects (array, function)");
3264 free(name);
3265 return 0;
3266 }
3267 char tarr[64];
3268 snprintf(tarr, sizeof(tarr), "__flt_arr_%d", g_temp_counter++);
3269 int larr = -1, garr = -1;
3270 if (g_locals) {
3271 larr = local_add(tarr);
3273 } else {
3274 garr = sym_index(tarr);
3276 }
3277 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3278 parser_fail(*pos, "filter expects (array, function)");
3279 free(name);
3280 return 0;
3281 }
3282 char tfn[64];
3283 snprintf(tfn, sizeof(tfn), "__flt_fn_%d", g_temp_counter++);
3284 int lfn = -1, gfn = -1;
3285 if (g_locals) {
3286 lfn = local_add(tfn);
3288 } else {
3289 gfn = sym_index(tfn);
3291 }
3293 char tres[64];
3294 snprintf(tres, sizeof(tres), "__flt_res_%d", g_temp_counter++);
3295 int lres = -1, gres = -1;
3296 if (g_locals) {
3297 lres = local_add(tres);
3299 } else {
3300 gres = sym_index(tres);
3302 }
3303 int c0 = bytecode_add_constant(bc, make_int(0));
3305 char ti[64];
3306 snprintf(ti, sizeof(ti), "__flt_i_%d", g_temp_counter++);
3307 int li = -1, gi = -1;
3308 if (g_locals) {
3309 li = local_add(ti);
3311 } else {
3312 gi = sym_index(ti);
3314 }
3315 int loop_start = bc->instr_count;
3316 if (g_locals) {
3319 } else {
3322 }
3326 if (g_locals) {
3329 } else {
3332 }
3334 if (g_locals) {
3336 } else {
3338 }
3341 /* if truthy then push elem to res */
3342 int jskip = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
3343 /* Append element to result: res[len(res)] = elem */
3344 /* Reload element into a temp */
3345 if (g_locals) {
3348 } else {
3351 }
3353
3354 char tvf[64];
3355 snprintf(tvf, sizeof(tvf), "__flt_v_%d", g_temp_counter++);
3356 int lvf = -1, gvf = -1;
3357 if (g_locals) {
3358 lvf = local_add(tvf);
3360 } else {
3361 gvf = sym_index(tvf);
3363 }
3364
3365 /* Push array */
3366 if (g_locals) {
3368 } else {
3370 }
3371
3372 /* index = len(res) */
3373 if (g_locals) {
3375 } else {
3377 }
3379
3380 /* value */
3381 if (g_locals) {
3383 } else {
3385 }
3386
3387 /* Append via insert: res.insert(index=len(res), value) */
3389 /* dApache-2.0ard returned new length */
3391
3392 int c1 = bytecode_add_constant(bc, make_int(1));
3393 /* patch skip over append */
3394 bytecode_set_operand(bc, jskip, bc->instr_count);
3395 /* i++ */
3396 if (g_locals) {
3401 } else {
3406 }
3407 bytecode_add_instruction(bc, OP_JUMP, loop_start);
3409 if (g_locals) {
3411 } else {
3413 }
3414 free(name);
3415 return 1;
3416 }
3417 if (strcmp(name, "reduce") == 0) {
3418 (*pos)++; /* '(' */
3419 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3420 parser_fail(*pos, "reduce expects (array, init, function)");
3421 free(name);
3422 return 0;
3423 }
3424 char tarr[64];
3425 snprintf(tarr, sizeof(tarr), "__red_arr_%d", g_temp_counter++);
3426 int larr = -1, garr = -1;
3427 if (g_locals) {
3428 larr = local_add(tarr);
3430 } else {
3431 garr = sym_index(tarr);
3433 }
3434 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3435 parser_fail(*pos, "reduce expects (array, init, function)");
3436 free(name);
3437 return 0;
3438 }
3439 char tacc[64];
3440 snprintf(tacc, sizeof(tacc), "__red_acc_%d", g_temp_counter++);
3441 int lacc = -1, gacc = -1;
3442 if (g_locals) {
3443 lacc = local_add(tacc);
3445 } else {
3446 gacc = sym_index(tacc);
3448 }
3449 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3450 parser_fail(*pos, "reduce expects (array, init, function)");
3451 free(name);
3452 return 0;
3453 }
3454 char tfn[64];
3455 snprintf(tfn, sizeof(tfn), "__red_fn_%d", g_temp_counter++);
3456 int lfn = -1, gfn = -1;
3457 if (g_locals) {
3458 lfn = local_add(tfn);
3460 } else {
3461 gfn = sym_index(tfn);
3463 }
3464 /* loop */
3465 int c0 = bytecode_add_constant(bc, make_int(0));
3467 char ti[64];
3468 snprintf(ti, sizeof(ti), "__red_i_%d", g_temp_counter++);
3469 int li = -1, gi = -1;
3470 if (g_locals) {
3471 li = local_add(ti);
3473 } else {
3474 gi = sym_index(ti);
3476 }
3477 int loop_start = bc->instr_count;
3478 if (g_locals) {
3481 } else {
3484 }
3488 /* elem */
3489 if (g_locals) {
3492 } else {
3495 }
3497
3498 /* Store elem to a temp so we can build stack as: fn, acc, elem */
3499 char telem[64];
3500 snprintf(telem, sizeof(telem), "__red_elem_%d", g_temp_counter++);
3501 int lelem = -1, gelem = -1;
3502 if (g_locals) {
3503 lelem = local_add(telem);
3505 } else {
3506 gelem = sym_index(telem);
3508 }
3509
3510 /* push function */
3511 if (g_locals) {
3513 } else {
3515 }
3516
3517 /* push accumulator (arg1) */
3518 if (g_locals) {
3520 } else {
3522 }
3523
3524 /* push element (arg2) */
3525 if (g_locals) {
3527 } else {
3529 }
3530
3531 /* call fn(acc, elem) -> result */
3533 /* store to acc */
3534 if (g_locals) {
3536 } else {
3538 }
3539 int c1 = bytecode_add_constant(bc, make_int(1));
3540 if (g_locals) {
3545 } else {
3550 }
3551 bytecode_add_instruction(bc, OP_JUMP, loop_start);
3553 /* result = acc on stack */
3554 if (g_locals) {
3556 } else {
3558 }
3559 free(name);
3560 return 1;
3561 }
3562 if (strcmp(name, "zip") == 0) {
3563 (*pos)++; /* '(' */
3564 if (!emit_expression(bc, src, len, pos)) {
3565 parser_fail(*pos, "zip expects first array");
3566 free(name);
3567 return 0;
3568 }
3569 if (*pos < len && src[*pos] == ',') {
3570 (*pos)++;
3571 skip_spaces(src, len, pos);
3572 } else {
3573 parser_fail(*pos, "zip expects 2 args");
3574 free(name);
3575 return 0;
3576 }
3577 if (!emit_expression(bc, src, len, pos)) {
3578 parser_fail(*pos, "zip expects second array");
3579 free(name);
3580 return 0;
3581 }
3582 if (!consume_char(src, len, pos, ')')) {
3583 parser_fail(*pos, "Expected ')' after zip args");
3584 free(name);
3585 return 0;
3586 }
3588 free(name);
3589 return 1;
3590 }
3591 /* math */
3592 if (strcmp(name, "min") == 0) {
3593 (*pos)++; /* '(' */
3594 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3595 parser_fail(*pos, "min expects 2 args");
3596 free(name);
3597 return 0;
3598 }
3599 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3600 parser_fail(*pos, "min expects 2 args");
3601 free(name);
3602 return 0;
3603 }
3605 free(name);
3606 return 1;
3607 }
3608 if (strcmp(name, "max") == 0) {
3609 (*pos)++; /* '(' */
3610 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3611 parser_fail(*pos, "max expects 2 args");
3612 free(name);
3613 return 0;
3614 }
3615 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3616 parser_fail(*pos, "max expects 2 args");
3617 free(name);
3618 return 0;
3619 }
3621 free(name);
3622 return 1;
3623 }
3624 if (strcmp(name, "fmin") == 0) {
3625 (*pos)++; /* '(' */
3626 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3627 parser_fail(*pos, "fmin expects 2 args");
3628 free(name);
3629 return 0;
3630 }
3631 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3632 parser_fail(*pos, "fmin expects 2 args");
3633 free(name);
3634 return 0;
3635 }
3637 free(name);
3638 return 1;
3639 }
3640 if (strcmp(name, "fmax") == 0) {
3641 (*pos)++; /* '(' */
3642 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3643 parser_fail(*pos, "fmax expects 2 args");
3644 free(name);
3645 return 0;
3646 }
3647 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3648 parser_fail(*pos, "fmax expects 2 args");
3649 free(name);
3650 return 0;
3651 }
3653 free(name);
3654 return 1;
3655 }
3656 if (strcmp(name, "clamp") == 0) {
3657 (*pos)++; /* '(' */
3658 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3659 parser_fail(*pos, "clamp expects 3 args");
3660 free(name);
3661 return 0;
3662 }
3663 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3664 parser_fail(*pos, "clamp expects 3 args");
3665 free(name);
3666 return 0;
3667 }
3668 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3669 parser_fail(*pos, "clamp expects 3 args");
3670 free(name);
3671 return 0;
3672 }
3674 free(name);
3675 return 1;
3676 }
3677 if (strcmp(name, "abs") == 0) {
3678 (*pos)++; /* '(' */
3679 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3680 parser_fail(*pos, "abs expects 1 arg");
3681 free(name);
3682 return 0;
3683 }
3685 free(name);
3686 return 1;
3687 }
3688 if (strcmp(name, "floor") == 0) {
3689 (*pos)++; /* '(' */
3690 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3691 parser_fail(*pos, "floor expects 1 arg");
3692 free(name);
3693 return 0;
3694 }
3696 free(name);
3697 return 1;
3698 }
3699 if (strcmp(name, "ceil") == 0) {
3700 (*pos)++; /* '(' */
3701 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3702 parser_fail(*pos, "ceil expects 1 arg");
3703 free(name);
3704 return 0;
3705 }
3707 free(name);
3708 return 1;
3709 }
3710 if (strcmp(name, "trunc") == 0) {
3711 (*pos)++; /* '(' */
3712 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3713 parser_fail(*pos, "trunc expects 1 arg");
3714 free(name);
3715 return 0;
3716 }
3718 free(name);
3719 return 1;
3720 }
3721 if (strcmp(name, "round") == 0) {
3722 (*pos)++; /* '(' */
3723 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3724 parser_fail(*pos, "round expects 1 arg");
3725 free(name);
3726 return 0;
3727 }
3729 free(name);
3730 return 1;
3731 }
3732 if (strcmp(name, "sin") == 0) {
3733 (*pos)++; /* '(' */
3734 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3735 parser_fail(*pos, "sin expects 1 arg");
3736 free(name);
3737 return 0;
3738 }
3740 free(name);
3741 return 1;
3742 }
3743 if (strcmp(name, "cos") == 0) {
3744 (*pos)++; /* '(' */
3745 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3746 parser_fail(*pos, "cos expects 1 arg");
3747 free(name);
3748 return 0;
3749 }
3751 free(name);
3752 return 1;
3753 }
3754 if (strcmp(name, "tan") == 0) {
3755 (*pos)++; /* '(' */
3756 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3757 parser_fail(*pos, "tan expects 1 arg");
3758 free(name);
3759 return 0;
3760 }
3762 free(name);
3763 return 1;
3764 }
3765 if (strcmp(name, "exp") == 0) {
3766 (*pos)++; /* '(' */
3767 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3768 parser_fail(*pos, "exp expects 1 arg");
3769 free(name);
3770 return 0;
3771 }
3773 free(name);
3774 return 1;
3775 }
3776 if (strcmp(name, "log") == 0) {
3777 (*pos)++; /* '(' */
3778 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3779 parser_fail(*pos, "log expects 1 arg");
3780 free(name);
3781 return 0;
3782 }
3784 free(name);
3785 return 1;
3786 }
3787 if (strcmp(name, "log10") == 0) {
3788 (*pos)++; /* '(' */
3789 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3790 parser_fail(*pos, "log10 expects 1 arg");
3791 free(name);
3792 return 0;
3793 }
3795 free(name);
3796 return 1;
3797 }
3798 if (strcmp(name, "sqrt") == 0) {
3799 (*pos)++; /* '(' */
3800 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3801 parser_fail(*pos, "sqrt expects 1 arg");
3802 free(name);
3803 return 0;
3804 }
3806 free(name);
3807 return 1;
3808 }
3809 if (strcmp(name, "gcd") == 0) {
3810 (*pos)++; /* '(' */
3811 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3812 parser_fail(*pos, "gcd expects 2 args");
3813 free(name);
3814 return 0;
3815 }
3816 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3817 parser_fail(*pos, "gcd expects 2 args");
3818 free(name);
3819 return 0;
3820 }
3822 free(name);
3823 return 1;
3824 }
3825 if (strcmp(name, "lcm") == 0) {
3826 (*pos)++; /* '(' */
3827 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3828 parser_fail(*pos, "lcm expects 2 args");
3829 free(name);
3830 return 0;
3831 }
3832 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3833 parser_fail(*pos, "lcm expects 2 args");
3834 free(name);
3835 return 0;
3836 }
3838 free(name);
3839 return 1;
3840 }
3841 if (strcmp(name, "isqrt") == 0) {
3842 (*pos)++; /* '(' */
3843 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3844 parser_fail(*pos, "isqrt expects 1 arg");
3845 free(name);
3846 return 0;
3847 }
3849 free(name);
3850 return 1;
3851 }
3852 if (strcmp(name, "sign") == 0) {
3853 (*pos)++; /* '(' */
3854 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3855 parser_fail(*pos, "sign expects 1 arg");
3856 free(name);
3857 return 0;
3858 }
3860 free(name);
3861 return 1;
3862 }
3863 if (strcmp(name, "pow") == 0) {
3864 (*pos)++; /* '(' */
3865 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3866 parser_fail(*pos, "pow expects 2 args");
3867 free(name);
3868 return 0;
3869 }
3870 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3871 parser_fail(*pos, "pow expects 2 args");
3872 free(name);
3873 return 0;
3874 }
3876 free(name);
3877 return 1;
3878 }
3879
3880 if (strcmp(name, "random_seed") == 0) {
3881 (*pos)++; /* '(' */
3882 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3883 parser_fail(*pos, "random_seed expects 1 arg");
3884 free(name);
3885 return 0;
3886 }
3888 free(name);
3889 return 1;
3890 }
3891 if (strcmp(name, "random_int") == 0) {
3892 (*pos)++; /* '(' */
3893 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3894 parser_fail(*pos, "random_int expects 2 args");
3895 free(name);
3896 return 0;
3897 }
3898 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3899 parser_fail(*pos, "random_int expects 2 args");
3900 free(name);
3901 return 0;
3902 }
3904 free(name);
3905 return 1;
3906 }
3907
3908 if (strcmp(name, "random_number") == 0) {
3909 (*pos)++; /* '(' */
3910 /* expects exactly 1 arg: length */
3911 if (!emit_expression(bc, src, len, pos)) {
3912 parser_fail(*pos, "random_number expects 1 arg (length)");
3913 free(name);
3914 return 0;
3915 }
3916 if (!consume_char(src, len, pos, ')')) {
3917 parser_fail(*pos, "Expected ')' after random_number arg");
3918 free(name);
3919 return 0;
3920 }
3922 free(name);
3923 return 1;
3924 }
3925
3926 /* threading */
3927 if (strcmp(name, "thread_spawn") == 0) {
3928 (*pos)++; /* '(' */
3929 /* thread_spawn(fn [, args]) */
3930 if (!emit_expression(bc, src, len, pos)) {
3931 parser_fail(*pos, "thread_spawn expects function as first arg");
3932 free(name);
3933 return 0;
3934 }
3935 int hasArgs = 0;
3936 skip_spaces(src, len, pos);
3937 if (*pos < len && src[*pos] == ',') {
3938 (*pos)++;
3939 skip_spaces(src, len, pos);
3940 if (!emit_expression(bc, src, len, pos)) {
3941 parser_fail(*pos, "thread_spawn second arg must be array or value");
3942 free(name);
3943 return 0;
3944 }
3945 hasArgs = 1;
3946 }
3947 if (!consume_char(src, len, pos, ')')) {
3948 parser_fail(*pos, "Expected ')' after thread_spawn args");
3949 free(name);
3950 return 0;
3951 }
3952 bytecode_add_instruction(bc, OP_THREAD_SPAWN, hasArgs ? 1 : 0);
3953 free(name);
3954 return 1;
3955 }
3956 if (strcmp(name, "thread_join") == 0) {
3957 (*pos)++; /* '(' */
3958 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3959 parser_fail(*pos, "thread_join expects 1 arg (thread id)");
3960 free(name);
3961 return 0;
3962 }
3964 free(name);
3965 return 1;
3966 }
3967 if (strcmp(name, "sleep") == 0) {
3968 (*pos)++; /* '(' */
3969 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3970 parser_fail(*pos, "sleep expects 1 arg (milliseconds)");
3971 free(name);
3972 return 0;
3973 }
3975 free(name);
3976 return 1;
3977 }
3978
3979 /* bitwise ops (32-bit) */
3980 if (strcmp(name, "band") == 0) {
3981 (*pos)++; /* '(' */
3982 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3983 parser_fail(*pos, "band expects 2 args");
3984 free(name);
3985 return 0;
3986 }
3987 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
3988 parser_fail(*pos, "band expects 2 args");
3989 free(name);
3990 return 0;
3991 }
3993 free(name);
3994 return 1;
3995 }
3996 if (strcmp(name, "bor") == 0) {
3997 (*pos)++; /* '(' */
3998 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
3999 parser_fail(*pos, "bor expects 2 args");
4000 free(name);
4001 return 0;
4002 }
4003 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
4004 parser_fail(*pos, "bor expects 2 args");
4005 free(name);
4006 return 0;
4007 }
4009 free(name);
4010 return 1;
4011 }
4012 if (strcmp(name, "bxor") == 0) {
4013 (*pos)++; /* '(' */
4014 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
4015 parser_fail(*pos, "bxor expects 2 args");
4016 free(name);
4017 return 0;
4018 }
4019 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
4020 parser_fail(*pos, "bxor expects 2 args");
4021 free(name);
4022 return 0;
4023 }
4025 free(name);
4026 return 1;
4027 }
4028 if (strcmp(name, "bnot") == 0) {
4029 (*pos)++; /* '(' */
4030 if (!emit_expression(bc, src, len, pos)) {
4031 parser_fail(*pos, "bnot expects 1 arg");
4032 free(name);
4033 return 0;
4034 }
4035 if (!consume_char(src, len, pos, ')')) {
4036 parser_fail(*pos, "bnot expects 1 arg");
4037 free(name);
4038 return 0;
4039 }
4041 free(name);
4042 return 1;
4043 }
4044 if (strcmp(name, "shl") == 0) {
4045 (*pos)++; /* '(' */
4046 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
4047 parser_fail(*pos, "shl expects 2 args");
4048 free(name);
4049 return 0;
4050 }
4051 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
4052 parser_fail(*pos, "shl expects 2 args");
4053 free(name);
4054 return 0;
4055 }
4057 free(name);
4058 return 1;
4059 }
4060 if (strcmp(name, "shr") == 0) {
4061 (*pos)++; /* '(' */
4062 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
4063 parser_fail(*pos, "shr expects 2 args");
4064 free(name);
4065 return 0;
4066 }
4067 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
4068 parser_fail(*pos, "shr expects 2 args");
4069 free(name);
4070 return 0;
4071 }
4073 free(name);
4074 return 1;
4075 }
4076 if (strcmp(name, "rol") == 0) {
4077 (*pos)++; /* '(' */
4078 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
4079 parser_fail(*pos, "rol expects 2 args");
4080 free(name);
4081 return 0;
4082 }
4083 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
4084 parser_fail(*pos, "rol expects 2 args");
4085 free(name);
4086 return 0;
4087 }
4089 free(name);
4090 return 1;
4091 }
4092 if (strcmp(name, "ror") == 0) {
4093 (*pos)++; /* '(' */
4094 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) {
4095 parser_fail(*pos, "ror expects 2 args");
4096 free(name);
4097 return 0;
4098 }
4099 if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) {
4100 parser_fail(*pos, "ror expects 2 args");
4101 free(name);
4102 return 0;
4103 }
4105 free(name);
4106 return 1;
4107 }
4108
4109 /* push function value first */
4110 if (local_idx < 0 && name_in_outer_envs(name)) {
4111 const char *fname = (bc && bc->name) ? bc->name : "<function>";
4112 parser_fail(*pos, "Nested function '%s' cannot access outer local '%s'. Pass it as a parameter instead.", fname, name);
4113 free(name);
4114 return 0;
4115 }
4116 if (local_idx >= 0) {
4118 } else {
4119 int gi = sym_index(name);
4121 }
4122 /* Track namespace alias only for the initial receiver; after any call it's no longer an alias value */
4123 int __ns_ctx = is_ns_alias(name);
4124 /* parse arguments */
4125 (*pos)++; /* '(' */
4126 int argc = 0;
4127 skip_spaces(src, len, pos);
4128 if (*pos < len && src[*pos] != ')') {
4129 do {
4130 if (!emit_expression(bc, src, len, pos)) {
4131 parser_fail(*pos, "Expected expression as function argument");
4132 free(name);
4133 return 0;
4134 }
4135 argc++;
4136 skip_spaces(src, len, pos);
4137 } while (*pos < len && src[*pos] == ',' && (++(*pos), skip_spaces(src, len, pos), 1));
4138 }
4139 if (!consume_char(src, len, pos, ')')) {
4140 parser_fail(*pos, "Expected ')' after arguments");
4141 free(name);
4142 return 0;
4143 }
4144#ifdef FUN_DEBUG
4145 /* DEBUG: show compiled call (only when FUN_DEBUG/FUN_TRACE enabled at runtime) */
4146 if (fun_debug_enabled()) {
4147 printf("compile: CALL %s with %d arg(s)\n", name, argc);
4148 }
4149#endif
4151 /* postfix indexing, slice, and dot access/method calls */
4152 for (;;) {
4153 skip_spaces(src, len, pos);
4154
4155 /* index/slice */
4156 if (*pos < len && src[*pos] == '[') {
4157 (*pos)++;
4158 if (!emit_expression(bc, src, len, pos)) {
4159 parser_fail(*pos, "Expected start expression");
4160 free(name);
4161 return 0;
4162 }
4163 skip_spaces(src, len, pos);
4164 if (*pos < len && src[*pos] == ':') {
4165 (*pos)++;
4166 skip_spaces(src, len, pos);
4167 size_t svp = *pos;
4168 if (!emit_expression(bc, src, len, pos)) {
4169 *pos = svp;
4170 int ci = bytecode_add_constant(bc, make_int(-1));
4172 }
4173 if (!consume_char(src, len, pos, ']')) {
4174 parser_fail(*pos, "Expected ']' after slice");
4175 free(name);
4176 return 0;
4177 }
4179 continue;
4180 } else {
4181 if (!consume_char(src, len, pos, ']')) {
4182 parser_fail(*pos, "Expected ']' after index");
4183 free(name);
4184 return 0;
4185 }
4187 continue;
4188 }
4189 }
4190
4191 /* dot property access and method-call sugar: obj.field or obj.method(...) */
4192 if (*pos < len && src[*pos] == '.') {
4193 (*pos)++; /* '.' */
4194 skip_spaces(src, len, pos);
4195 char *mname = NULL;
4196 if (!read_identifier_into(src, len, pos, &mname)) {
4197 parser_fail(*pos, "Expected identifier after '.'");
4198 free(name);
4199 return 0;
4200 }
4201 int is_private = (mname && mname[0] == '_');
4202 int kci = bytecode_add_constant(bc, make_string(mname));
4203
4204 /* Peek for immediate call: obj.method( ... ) */
4205 size_t callp = *pos;
4206 skip_spaces(src, len, &callp);
4207 if (callp < len && src[callp] == '(') {
4208 /* If private method on non-'this' receiver in this context -> error */
4209 if (is_private) {
4210 char msg[160];
4211 snprintf(msg, sizeof(msg), "AccessError: private method '%s' is not accessible here", mname);
4212 int ci = bytecode_add_constant(bc, make_string(msg));
4216 free(mname);
4217 continue;
4218 }
4219
4220 /* After a function call, the receiver is a value (not a namespace alias);
4221 always treat dot-call as a method with implicit 'this'. */
4222 int is_ns = 0;
4223
4224 /* Method sugar with implicit 'this' */
4227 bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* -> stack: obj, func */
4228 bytecode_add_instruction(bc, OP_SWAP, 0); /* -> stack: func, obj (this) */
4229
4230 /* Consume '(' and parse args */
4231 *pos = callp + 1;
4232 int argc = 0;
4233 skip_spaces(src, len, pos);
4234 if (*pos < len && src[*pos] != ')') {
4235 do {
4236 if (!emit_expression(bc, src, len, pos)) {
4237 parser_fail(*pos, "Expected expression as method argument");
4238 free(mname);
4239 free(name);
4240 return 0;
4241 }
4242 argc++;
4243 skip_spaces(src, len, pos);
4244 } while (*pos < len && src[*pos] == ',' && (++(*pos), skip_spaces(src, len, pos), 1));
4245 }
4246 if (!consume_char(src, len, pos, ')')) {
4247 parser_fail(*pos, "Expected ')' after arguments");
4248 free(mname);
4249 free(name);
4250 return 0;
4251 }
4252
4253 /* Call */
4254 bytecode_add_instruction(bc, OP_CALL, is_ns ? argc : (argc + 1));
4255 free(mname);
4256 continue;
4257 } else {
4258 /* Plain property get: obj["field"] */
4261 free(mname);
4262 continue;
4263 }
4264 }
4265
4266 break;
4267 }
4268 free(name);
4269 return 1;
4270 } else {
4271 if (local_idx < 0 && name_in_outer_envs(name)) {
4272 const char *fname = (bc && bc->name) ? bc->name : "<function>";
4273 parser_fail(*pos, "Nested function '%s' cannot access outer local '%s'. Pass it as a parameter instead.", fname, name);
4274 free(name);
4275 return 0;
4276 }
4277 if (local_idx >= 0) {
4279 } else {
4280 int gi = sym_index(name);
4282 }
4283 /* track namespace alias only for the initial receiver; after any call it's no longer an alias value */
4284 int __ns_ctx = is_ns_alias(name);
4285 /* postfix indexing, slice, and dot access/method calls */
4286 for (;;) {
4287 skip_spaces(src, len, pos);
4288
4289 /* index/slice */
4290 if (*pos < len && src[*pos] == '[') {
4291 (*pos)++;
4292 if (!emit_expression(bc, src, len, pos)) {
4293 parser_fail(*pos, "Expected start expression");
4294 free(name);
4295 return 0;
4296 }
4297 skip_spaces(src, len, pos);
4298 if (*pos < len && src[*pos] == ':') {
4299 (*pos)++;
4300 skip_spaces(src, len, pos);
4301 size_t svp = *pos;
4302 if (!emit_expression(bc, src, len, pos)) {
4303 *pos = svp;
4304 int ci = bytecode_add_constant(bc, make_int(-1));
4306 }
4307 if (!consume_char(src, len, pos, ']')) {
4308 parser_fail(*pos, "Expected ']' after slice");
4309 free(name);
4310 return 0;
4311 }
4313 continue;
4314 } else {
4315 if (!consume_char(src, len, pos, ']')) {
4316 parser_fail(*pos, "Expected ']' after index");
4317 free(name);
4318 return 0;
4319 }
4321 continue;
4322 }
4323 }
4324
4325 /* dot property access and method-call sugar */
4326 if (*pos < len && src[*pos] == '.') {
4327 (*pos)++;
4328 skip_spaces(src, len, pos);
4329 char *mname = NULL;
4330 if (!read_identifier_into(src, len, pos, &mname)) {
4331 parser_fail(*pos, "Expected identifier after '.'");
4332 free(name);
4333 return 0;
4334 }
4335 int is_private = (mname && mname[0] == '_');
4336 int kci = bytecode_add_constant(bc, make_string(mname));
4337
4338 /* Peek for call */
4339 size_t callp = *pos;
4340 skip_spaces(src, len, &callp);
4341 if (callp < len && src[callp] == '(') {
4342 /* If private and receiver is not 'this' -> error */
4343 if (is_private && !(strcmp(name, "this") == 0)) {
4344 char msg[160];
4345 snprintf(msg, sizeof(msg), "AccessError: private method '%s' is not accessible", mname);
4346 int ci = bytecode_add_constant(bc, make_string(msg));
4350 free(mname);
4351 continue;
4352 }
4353
4354 /* Use initial alias context for only the first dot-call; reset after call */
4355 int is_ns = __ns_ctx;
4356
4357 if (!is_ns) {
4358 /* Method sugar with implicit 'this' */
4361 bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* -> obj, func */
4362 bytecode_add_instruction(bc, OP_SWAP, 0); /* -> func, obj */
4363 } else {
4364 /* Plain property function call */
4366 bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* -> func */
4367 }
4368
4369 *pos = callp + 1;
4370 int argc = 0;
4371 skip_spaces(src, len, pos);
4372 if (*pos < len && src[*pos] != ')') {
4373 do {
4374 if (!emit_expression(bc, src, len, pos)) {
4375 parser_fail(*pos, "Expected expression as method argument");
4376 free(mname);
4377 free(name);
4378 return 0;
4379 }
4380 argc++;
4381 skip_spaces(src, len, pos);
4382 } while (*pos < len && src[*pos] == ',' && (++(*pos), skip_spaces(src, len, pos), 1));
4383 }
4384 if (!consume_char(src, len, pos, ')')) {
4385 parser_fail(*pos, "Expected ')' after arguments");
4386 free(mname);
4387 free(name);
4388 return 0;
4389 }
4390
4391 bytecode_add_instruction(bc, OP_CALL, is_ns ? argc : (argc + 1));
4392 /* After any call, the receiver is now a value, not a namespace alias */
4393 __ns_ctx = 0;
4394
4395 free(mname);
4396 continue;
4397 } else {
4398 /* plain property get (allowed even for private name) */
4401 free(mname);
4402 continue;
4403 }
4404 }
4405
4406 break;
4407 }
4408 free(name);
4409 return 1;
4410 }
4411 }
4412
4413 return 0;
4414}
4415
4428static int emit_unary(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4429 skip_spaces(src, len, pos);
4430 if (*pos < len && src[*pos] == '!') {
4431 (*pos)++;
4432 if (!emit_unary(bc, src, len, pos)) {
4433 parser_fail(*pos, "Expected expression after '!'");
4434 return 0;
4435 }
4437 return 1;
4438 }
4439 if (*pos < len && src[*pos] == '-') {
4440 (*pos)++;
4441 /* unary minus -> 0 - expr */
4442 int ci = bytecode_add_constant(bc, make_int(0));
4444 if (!emit_unary(bc, src, len, pos)) {
4445 parser_fail(*pos, "Expected expression after unary '-'");
4446 return 0;
4447 }
4449 return 1;
4450 }
4451 return emit_primary(bc, src, len, pos);
4452}
4453
4465static int emit_multiplicative(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4466 if (!emit_unary(bc, src, len, pos)) return 0;
4467 for (;;) {
4468 skip_spaces(src, len, pos);
4469
4470 /* Stop expression at start of inline comment */
4471 if (*pos + 1 < len && src[*pos] == '/' && src[*pos + 1] == '/') {
4472 break;
4473 }
4474 /* Skip block comments inside expressions */
4475 if (*pos + 1 < len && src[*pos] == '/' && src[*pos + 1] == '*') {
4476 size_t p = *pos + 2;
4477 while (p + 1 < len && !(src[p] == '*' && src[p + 1] == '/')) {
4478 p++;
4479 }
4480 if (p + 1 < len) p += 2; /* consume closing marker */
4481 *pos = p;
4482 continue;
4483 }
4484
4485 if (*pos < len && src[*pos] == '*') {
4486 (*pos)++;
4487 if (!emit_unary(bc, src, len, pos)) {
4488 parser_fail(*pos, "Expected expression after '*'");
4489 return 0;
4490 }
4492 continue;
4493 }
4494 if (*pos < len && src[*pos] == '/') {
4495 (*pos)++;
4496 if (!emit_unary(bc, src, len, pos)) {
4497 parser_fail(*pos, "Expected expression after '/'");
4498 return 0;
4499 }
4501 continue;
4502 }
4503 if (*pos < len && src[*pos] == '%') {
4504 (*pos)++;
4505 if (!emit_unary(bc, src, len, pos)) {
4506 parser_fail(*pos, "Expected expression after '%'");
4507 return 0;
4508 }
4510 continue;
4511 }
4512 break;
4513 }
4514 return 1;
4515}
4516
4528static int emit_additive(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4529 if (!emit_multiplicative(bc, src, len, pos)) return 0;
4530 for (;;) {
4531 skip_spaces(src, len, pos);
4532 if (*pos < len && src[*pos] == '+') {
4533 (*pos)++;
4534 if (!emit_multiplicative(bc, src, len, pos)) {
4535 parser_fail(*pos, "Expected expression after '+'");
4536 return 0;
4537 }
4539 continue;
4540 }
4541 if (*pos < len && src[*pos] == '-') {
4542 (*pos)++;
4543 if (!emit_multiplicative(bc, src, len, pos)) {
4544 parser_fail(*pos, "Expected expression after '-'");
4545 return 0;
4546 }
4548 continue;
4549 }
4550 break;
4551 }
4552 return 1;
4553}
4554
4566static int emit_relational(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4567 if (!emit_additive(bc, src, len, pos)) return 0;
4568 for (;;) {
4569 skip_spaces(src, len, pos);
4570 if (*pos + 1 < len && src[*pos] == '<' && src[*pos + 1] == '=') {
4571 *pos += 2;
4572 if (!emit_additive(bc, src, len, pos)) {
4573 parser_fail(*pos, "Expected expression after '<='");
4574 return 0;
4575 }
4577 continue;
4578 }
4579 if (*pos + 1 < len && src[*pos] == '>' && src[*pos + 1] == '=') {
4580 *pos += 2;
4581 if (!emit_additive(bc, src, len, pos)) {
4582 parser_fail(*pos, "Expected expression after '>='");
4583 return 0;
4584 }
4586 continue;
4587 }
4588 if (*pos < len && src[*pos] == '<') {
4589 (*pos)++;
4590 if (!emit_additive(bc, src, len, pos)) {
4591 parser_fail(*pos, "Expected expression after '<'");
4592 return 0;
4593 }
4595 continue;
4596 }
4597 if (*pos < len && src[*pos] == '>') {
4598 (*pos)++;
4599 if (!emit_additive(bc, src, len, pos)) {
4600 parser_fail(*pos, "Expected expression after '>'");
4601 return 0;
4602 }
4604 continue;
4605 }
4606 break;
4607 }
4608 return 1;
4609}
4610
4622static int emit_equality(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4623 if (!emit_relational(bc, src, len, pos)) return 0;
4624 for (;;) {
4625 skip_spaces(src, len, pos);
4626 if (*pos + 1 < len && src[*pos] == '=' && src[*pos + 1] == '=') {
4627 *pos += 2;
4628 if (!emit_relational(bc, src, len, pos)) {
4629 parser_fail(*pos, "Expected expression after '=='");
4630 return 0;
4631 }
4633 continue;
4634 }
4635 if (*pos + 1 < len && src[*pos] == '!' && src[*pos + 1] == '=') {
4636 *pos += 2;
4637 if (!emit_relational(bc, src, len, pos)) {
4638 parser_fail(*pos, "Expected expression after '!='");
4639 return 0;
4640 }
4642 continue;
4643 }
4644 break;
4645 }
4646 return 1;
4647}
4648
4661static int emit_and_expr(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4662 int jf_idxs[64];
4663 int jf_count = 0;
4664 int has_and = 0;
4665
4666 /* first operand */
4667 if (!emit_equality(bc, src, len, pos)) return 0;
4668
4669 for (;;) {
4670 skip_spaces(src, len, pos);
4671 if (!(*pos + 1 < len && src[*pos] == '&' && src[*pos + 1] == '&')) break;
4672 *pos += 2;
4673 has_and = 1;
4674
4675 /* if current value is false -> jump to false label (patched later) */
4676 if (jf_count < (int)(sizeof(jf_idxs) / sizeof(jf_idxs[0]))) {
4677 jf_idxs[jf_count++] = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
4678 } else {
4679 parser_fail(*pos, "Too many operands in '&&' chain");
4680 return 0;
4681 }
4682
4683 /* evaluate next operand */
4684 if (!emit_equality(bc, src, len, pos)) {
4685 parser_fail(*pos, "Expected expression after '&&'");
4686 return 0;
4687 }
4688 }
4689
4690 if (has_and) {
4691 /* final: if last operand is false -> jump false */
4692 if (jf_count < (int)(sizeof(jf_idxs) / sizeof(jf_idxs[0]))) {
4693 jf_idxs[jf_count++] = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
4694 } else {
4695 parser_fail(*pos, "Too many operands in '&&' chain");
4696 return 0;
4697 }
4698
4699 /* all were truthy -> result true */
4700 int c1 = bytecode_add_constant(bc, make_bool(1));
4702 int j_end = bytecode_add_instruction(bc, OP_JUMP, 0);
4703
4704 /* false label: patch all false jumps here, result false */
4705 int l_false = bc->instr_count;
4706 for (int i = 0; i < jf_count; ++i) {
4707 bytecode_set_operand(bc, jf_idxs[i], l_false);
4708 }
4709 int c0 = bytecode_add_constant(bc, make_bool(0));
4711
4712 /* end */
4713 int l_end = bc->instr_count;
4714 bytecode_set_operand(bc, j_end, l_end);
4715 }
4716
4717 return 1;
4718}
4719
4732static int emit_or_expr(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4733 int true_jumps[64];
4734 int tj_count = 0;
4735 int has_or = 0;
4736
4737 /* first operand */
4738 if (!emit_and_expr(bc, src, len, pos)) return 0;
4739
4740 for (;;) {
4741 skip_spaces(src, len, pos);
4742 if (!(*pos + 1 < len && src[*pos] == '|' && src[*pos + 1] == '|')) break;
4743 *pos += 2;
4744 has_or = 1;
4745
4746 /* if current value is false -> proceed to next; else -> result true */
4747 int jf_proceed = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
4748
4749 /* true path: push true and jump to end */
4750 int c1 = bytecode_add_constant(bc, make_bool(1));
4752 if (tj_count < (int)(sizeof(true_jumps) / sizeof(true_jumps[0]))) {
4753 true_jumps[tj_count++] = bytecode_add_instruction(bc, OP_JUMP, 0);
4754 } else {
4755 parser_fail(*pos, "Too many operands in '||' chain");
4756 return 0;
4757 }
4758
4759 /* patch to start of next operand */
4760 bytecode_set_operand(bc, jf_proceed, bc->instr_count);
4761
4762 /* evaluate next operand */
4763 if (!emit_and_expr(bc, src, len, pos)) {
4764 parser_fail(*pos, "Expected expression after '||'");
4765 return 0;
4766 }
4767 }
4768
4769 if (has_or) {
4770 /* After evaluating the last operand: test it and produce 1/0 */
4771 int jf_last = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
4772
4773 int c1 = bytecode_add_constant(bc, make_int(1));
4775 int j_end_single = bytecode_add_instruction(bc, OP_JUMP, 0);
4776
4777 int l_false = bc->instr_count;
4778 bytecode_set_operand(bc, jf_last, l_false);
4779 int c0 = bytecode_add_constant(bc, make_int(0));
4781
4782 int l_end = bc->instr_count;
4783 bytecode_set_operand(bc, j_end_single, l_end);
4784 for (int i = 0; i < tj_count; ++i) {
4785 bytecode_set_operand(bc, true_jumps[i], l_end);
4786 }
4787 }
4788
4789 return 1;
4790}
4791
4803static int emit_conditional(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4804 /* parse condition (logical OR precedence or higher) */
4805 if (!emit_or_expr(bc, src, len, pos)) return 0;
4806
4807 for (;;) {
4808 skip_spaces(src, len, pos);
4809 if (!(*pos < len && src[*pos] == '?')) break;
4810 (*pos)++; /* consume '?' */
4811
4812 /* If condition is false -> jump to false arm */
4813 int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
4814
4815 /* true arm (right-assoc: allow nested ternaries) */
4816 skip_spaces(src, len, pos);
4817 if (!emit_conditional(bc, src, len, pos)) {
4818 parser_fail(*pos, "Expected expression after '?'");
4819 return 0;
4820 }
4821
4822 /* After true arm, unconditionally skip false arm */
4823 int jmp_end = bytecode_add_instruction(bc, OP_JUMP, 0);
4824
4825 /* false arm label */
4826 bytecode_set_operand(bc, jmp_false, bc->instr_count);
4827
4828 /* require ':' */
4829 skip_spaces(src, len, pos);
4830 if (!(*pos < len && src[*pos] == ':')) {
4831 parser_fail(*pos, "Expected ':' in conditional expression");
4832 return 0;
4833 }
4834 (*pos)++; /* consume ':' */
4835
4836 /* false arm (right-assoc) */
4837 skip_spaces(src, len, pos);
4838 if (!emit_conditional(bc, src, len, pos)) {
4839 parser_fail(*pos, "Expected expression after ':'");
4840 return 0;
4841 }
4842
4843 /* end label */
4844 bytecode_set_operand(bc, jmp_end, bc->instr_count);
4845 /* loop to allow chaining like a ? b : c ? d : e (right-assoc) */
4846 }
4847 return 1;
4848}
4849
4850/* top-level expression */
4864static int emit_expression(Bytecode *bc, const char *src, size_t len, size_t *pos) {
4865 return emit_conditional(bc, src, len, pos);
4866}
4867
4868/*
4869 * Indentation-aware compiler (2 spaces):
4870 * - Optional shebang and a single fun <ident>() { ... } wrapper.
4871 * - Statements supported: print(expr), ident = expr, and if <expr> with an indented block.
4872 * - Literals: strings, integers, booleans; identifiers are globals.
4873 * - Ends with HALT.
4874 */
4875
4876/* line/indent utilities */
4887static void skip_to_eol(const char *src, size_t len, size_t *pos) {
4888 /* Strict mode: only allow trailing spaces and comments until end-of-line. */
4889 size_t p = *pos;
4890
4891 for (;;) {
4892 /* skip spaces */
4893 while (p < len && src[p] == ' ')
4894 p++;
4895
4896 if (p >= len) {
4897 *pos = p;
4898 return;
4899 }
4900 /* Treat CR, LF, and CRLF as end-of-line */
4901 if (src[p] == '\r') {
4902 p++;
4903 if (p < len && src[p] == '\n') p++;
4904 *pos = p;
4905 return;
4906 }
4907 if (src[p] == '\n') {
4908 *pos = p + 1;
4909 return;
4910 }
4911
4912 /* line or block comments allowed */
4913 if (p + 1 < len && src[p] == '/' && src[p + 1] == '/') {
4914 /* consume rest of line up to CR or LF */
4915 p += 2;
4916 while (p < len && src[p] != '\n' && src[p] != '\r')
4917 p++;
4918 if (p < len && src[p] == '\r') {
4919 p++;
4920 if (p < len && src[p] == '\n') p++;
4921 } else if (p < len && src[p] == '\n') {
4922 p++;
4923 }
4924 *pos = p;
4925 return;
4926 }
4927
4928 if (p + 1 < len && src[p] == '/' && src[p + 1] == '*') {
4929 /* consume block comment, then loop again for spaces till EOL */
4930 p += 2;
4931 while (p + 1 < len && !(src[p] == '*' && src[p + 1] == '/')) {
4932 p++;
4933 }
4934 if (p + 1 < len) {
4935 p += 2; /* consume closing */
4936 continue;
4937 } else {
4938 parser_fail(p, "Unterminated block comment at end of file");
4939 *pos = p;
4940 return;
4941 }
4942 }
4943
4944 /* Any other character here is unexpected trailing garbage */
4945 parser_fail(p, "Unexpected trailing characters at end of line");
4946 *pos = p;
4947 return;
4948 }
4949}
4950
4975static int read_line_start(const char *src, size_t len, size_t *pos, int *out_indent) {
4976 while (*pos < len) {
4977 size_t p = *pos;
4978 int spaces = 0;
4979 while (p < len && src[p] == ' ') {
4980 spaces++;
4981 p++;
4982 }
4983 if (p < len && src[p] == '\t') {
4984 parser_fail(p, "Tabs are forbidden for indentation");
4985 return 0;
4986 }
4987 if (p >= len) {
4988 *pos = p;
4989 return 0;
4990 }
4991
4992 /* empty line: handle CR, LF, and CRLF */
4993 if (src[p] == '\r') {
4994 p++;
4995 if (p < len && src[p] == '\n') p++;
4996 *pos = p;
4997 continue;
4998 }
4999 if (src[p] == '\n') {
5000 p++;
5001 *pos = p;
5002 continue;
5003 }
5004
5005 /* // comment-only line */
5006 if (p + 1 < len && src[p] == '/' && src[p + 1] == '/') {
5007 /* skip entire line up to CR/LF */
5008 p += 2;
5009 while (p < len && src[p] != '\n' && src[p] != '\r')
5010 p++;
5011 if (p < len && src[p] == '\r') {
5012 p++;
5013 if (p < len && src[p] == '\n') p++;
5014 } else if (p < len && src[p] == '\n') {
5015 p++;
5016 }
5017 *pos = p;
5018 continue;
5019 }
5020
5021 // block comment starting at line (treat as comment-only line)
5022 if (p + 1 < len && src[p] == '/' && src[p + 1] == '*') {
5023 p += 2;
5024 /* advance until we find closing block comment marker */
5025 while (p + 1 < len && !(src[p] == '*' && src[p + 1] == '/')) {
5026 p++;
5027 }
5028 if (p + 1 < len) p += 2; /* consume closing block comment marker */
5029 /* consume to end of current line (if any leftover) */
5030 while (p < len && src[p] != '\n' && src[p] != '\r')
5031 p++;
5032 if (p < len && src[p] == '\r') {
5033 p++;
5034 if (p < len && src[p] == '\n') p++;
5035 } else if (p < len && src[p] == '\n') {
5036 p++;
5037 }
5038 *pos = p;
5039 continue;
5040 }
5041
5042 if (spaces % 2 != 0) {
5043 parser_fail(p, "Indentation must be multiples of two spaces");
5044 return 0;
5045 }
5046 *out_indent = spaces / 2;
5047 *pos = p; /* point to first code char */
5048 return 1;
5049 }
5050 return 0;
5051}
5052
5053/* forward decl */
5054static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos, int current_indent);
5055
5056/* parse and emit a single simple (non-if) statement on the current line */
5068static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, size_t *pos) {
5069 size_t local_pos = *pos;
5070 char *name = NULL;
5071 if (read_identifier_into(src, len, &local_pos, &name)) {
5072
5073 /* alias: accept 'sint*' as synonyms for 'int*' */
5074 if (strcmp(name, "sint8") == 0) {
5075 free(name);
5076 name = strdup("int8");
5077 } else if (strcmp(name, "sint16") == 0) {
5078 free(name);
5079 name = strdup("int16");
5080 } else if (strcmp(name, "sint32") == 0) {
5081 free(name);
5082 name = strdup("int32");
5083 } else if (strcmp(name, "sint64") == 0) {
5084 free(name);
5085 name = strdup("int64");
5086 }
5087
5088 /* return statement */
5089 if (strcmp(name, "return") == 0) {
5090 free(name);
5091 skip_spaces(src, len, &local_pos);
5092 /* optional expression */
5093 size_t save_pos = local_pos;
5094 if (emit_expression(bc, src, len, &local_pos)) {
5095 /* expression result already on stack */
5096 } else {
5097 /* no expression: return nil */
5098 local_pos = save_pos;
5099 int ci = bytecode_add_constant(bc, make_nil());
5101 }
5103 *pos = local_pos;
5104 skip_to_eol(src, len, pos);
5105 return;
5106 }
5107
5108 /* exit statement: exit [expr]? */
5109 if (strcmp(name, "exit") == 0) {
5110 free(name);
5111 skip_spaces(src, len, &local_pos);
5112 size_t save_pos = local_pos;
5113 if (emit_expression(bc, src, len, &local_pos)) {
5114 /* expression result already on stack */
5115 } else {
5116 /* default exit code 0 */
5117 local_pos = save_pos;
5118 int ci = bytecode_add_constant(bc, make_int(0));
5120 }
5122 *pos = local_pos;
5123 skip_to_eol(src, len, pos);
5124 return;
5125 }
5126
5127 /* break / continue */
5128 if (strcmp(name, "break") == 0) {
5129 free(name);
5130 if (!g_loop_ctx) {
5131 parser_fail(local_pos, "break used outside of loop");
5132 return;
5133 }
5134 int j = bytecode_add_instruction(bc, OP_JUMP, 0);
5135 if (g_loop_ctx->break_count < (int)(sizeof(g_loop_ctx->break_jumps) / sizeof(g_loop_ctx->break_jumps[0]))) {
5136 g_loop_ctx->break_jumps[g_loop_ctx->break_count++] = j;
5137 } else {
5138 parser_fail(local_pos, "Too many 'break' in one loop");
5139 return;
5140 }
5141 *pos = local_pos;
5142 skip_to_eol(src, len, pos);
5143 return;
5144 }
5145 if (strcmp(name, "continue") == 0) {
5146 free(name);
5147 if (!g_loop_ctx) {
5148 parser_fail(local_pos, "continue used outside of loop");
5149 return;
5150 }
5151 int j = bytecode_add_instruction(bc, OP_JUMP, 0);
5152 if (g_loop_ctx->cont_count < (int)(sizeof(g_loop_ctx->continue_jumps) / sizeof(g_loop_ctx->continue_jumps[0]))) {
5153 g_loop_ctx->continue_jumps[g_loop_ctx->cont_count++] = j;
5154 } else {
5155 parser_fail(local_pos, "Too many 'continue' in one loop");
5156 return;
5157 }
5158 *pos = local_pos;
5159 skip_to_eol(src, len, pos);
5160 return;
5161 }
5162
5163 /* typed declarations:
5164 number|string|boolean|nil|Class|byte|uint8|uint16|uint32|uint64|int8|int16|int32|int64 <ident> (= expr)?
5165 Note: 'number' maps to signed 64-bit here. 'byte' is an alias of unsigned 8-bit. 'Class' restricts to class instances.
5166 */
5167 if (strcmp(name, "number") == 0 || strcmp(name, "string") == 0 || strcmp(name, "boolean") == 0 || strcmp(name, "nil") == 0 || strcmp(name, "class") == 0 || strcmp(name, "float") == 0 || strcmp(name, "array") == 0 || strcmp(name, "byte") == 0 || strcmp(name, "uint8") == 0 || strcmp(name, "uint16") == 0 || strcmp(name, "uint32") == 0 || strcmp(name, "uint64") == 0 || strcmp(name, "int8") == 0 || strcmp(name, "int16") == 0 || strcmp(name, "int32") == 0 || strcmp(name, "int64") == 0) {
5168 int is_number = (strcmp(name, "number") == 0);
5169 int is_string = (strcmp(name, "string") == 0);
5170 int is_boolean = (strcmp(name, "boolean") == 0);
5171 int is_nil = (strcmp(name, "nil") == 0);
5172 int is_class = (strcmp(name, "class") == 0);
5173 int is_float = (strcmp(name, "float") == 0);
5174 int is_array = (strcmp(name, "array") == 0);
5175 int is_byte = (strcmp(name, "byte") == 0);
5176 int is_u8 = (strcmp(name, "uint8") == 0) || is_byte;
5177 int is_u16 = (strcmp(name, "uint16") == 0);
5178 int is_u32 = (strcmp(name, "uint32") == 0);
5179 int is_u64 = (strcmp(name, "uint64") == 0);
5180 int is_s8 = (strcmp(name, "int8") == 0);
5181 int is_s16 = (strcmp(name, "int16") == 0);
5182 int is_s32 = (strcmp(name, "int32") == 0);
5183 int is_s64 = (strcmp(name, "int64") == 0) || is_number; /* number maps to int64 (signed) */
5184 int decl_bits = is_u8 ? 8 : is_u16 ? 16
5185 : is_u32 ? 32
5186 : is_u64 ? 64
5187 : is_s8 ? 8
5188 : is_s16 ? 16
5189 : is_s32 ? 32
5190 : is_s64 ? 64
5191 : 0;
5192 int decl_signed = (is_s8 || is_s16 || is_s32 || is_s64) ? 1 : 0;
5193 /* store decl bits with sign encoded: negative means signed (number is signed 64-bit) */
5194 if (decl_signed) decl_bits = -decl_bits;
5195
5196 /* declared type metadata: integers use decl_bits; string/boolean/nil/Class/float/array use special markers */
5197 int decl_meta = decl_bits;
5198 if (is_string) {
5199 decl_meta = TYPE_META_STRING;
5200 } else if (is_boolean) {
5201 decl_meta = TYPE_META_BOOLEAN;
5202 } else if (is_nil) {
5203 decl_meta = TYPE_META_NIL;
5204 } else if (is_class) {
5205 decl_meta = TYPE_META_CLASS;
5206 } else if (is_float) {
5207 decl_meta = TYPE_META_FLOAT;
5208 } else if (is_array) {
5209 decl_meta = TYPE_META_ARRAY;
5210 }
5211
5212 free(name);
5213
5214 /* read variable name */
5215 char *varname = NULL;
5216 skip_spaces(src, len, &local_pos);
5217 if (!read_identifier_into(src, len, &local_pos, &varname)) {
5218 parser_fail(local_pos, "Expected identifier after type declaration");
5219 return;
5220 }
5221
5222 /* decide local vs global */
5223 int lidx = -1;
5224 int gi = -1;
5225 if (g_locals) {
5226 int existing = local_find(varname);
5227 if (existing >= 0)
5228 lidx = existing;
5229 else
5230 lidx = local_add(varname);
5231 if (lidx >= 0) {
5232 g_locals->types[lidx] = decl_meta; /* encoding: ±bits for integers; TYPE_META_* for non-integer enforced types; 0 = dynamic */
5233 }
5234 } else {
5235 gi = sym_index(varname);
5236 if (gi >= 0) {
5237 G.types[gi] = decl_meta;
5238 }
5239 }
5240 free(varname);
5241
5242 skip_spaces(src, len, &local_pos);
5243 if (local_pos < len && src[local_pos] == '=') {
5244 local_pos++; /* '=' */
5245 if (!emit_expression(bc, src, len, &local_pos)) {
5246 parser_fail(local_pos, "Expected initializer expression after '='");
5247 return;
5248 }
5249
5250 /* Enforce declared type on initializer */
5251 if (decl_meta == TYPE_META_STRING) {
5252 /* expect String */
5255 int ciExp = bytecode_add_constant(bc, make_string("String"));
5258 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5259 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5260 /* error block */
5261 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5262 {
5263 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected String"));
5267 }
5268 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5269 } else if (decl_meta == TYPE_META_CLASS) {
5270 /* expect Class instance: Map with "__class" key */
5271 /* Check typeof(v) == "Map" */
5274 {
5275 int ciMap = bytecode_add_constant(bc, make_string("Map"));
5277 }
5279 int j_err1 = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5280 /* Check v has "__class" */
5282 {
5283 int kci = bytecode_add_constant(bc, make_string("__class"));
5285 }
5287 int j_err2 = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5288 /* Success path jumps over error block */
5289 int j_ok = bytecode_add_instruction(bc, OP_JUMP, 0);
5290 /* Error block */
5291 int err_lbl = bc->instr_count;
5292 bytecode_set_operand(bc, j_err1, err_lbl);
5293 bytecode_set_operand(bc, j_err2, err_lbl);
5294 {
5295 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Class"));
5299 }
5300 /* Continue after checks */
5301 bytecode_set_operand(bc, j_ok, bc->instr_count);
5302 } else if (decl_meta == TYPE_META_FLOAT) {
5303 /* expect Float */
5306 int ciF = bytecode_add_constant(bc, make_string("Float"));
5309 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5310 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5311 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5312 {
5313 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Float"));
5317 }
5318 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5319 } else if (decl_meta == TYPE_META_ARRAY) {
5320 /* expect Array */
5323 int ciArr = bytecode_add_constant(bc, make_string("Array"));
5326 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5327 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5328 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5329 {
5330 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Array"));
5334 }
5335 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5336 } else if (decl_meta == TYPE_META_BOOLEAN) {
5337 /* accept Boolean literal or Number; if Number, clamp to 0/1 */
5338 /* check if value is Boolean */
5341 int ciBool = bytecode_add_constant(bc, make_string("Boolean"));
5344 int j_not_bool = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5345 /* it is Boolean -> OK, skip number check */
5346 int j_done = bytecode_add_instruction(bc, OP_JUMP, 0);
5347 /* not Boolean: check Number */
5348 bytecode_set_operand(bc, j_not_bool, bc->instr_count);
5351 int ciNum = bytecode_add_constant(bc, make_string("Number"));
5354 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5355 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5356 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5357 {
5358 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Boolean or Number for boolean"));
5362 }
5363 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5364 /* if Number, clamp to 0/1 */
5366 /* common continuation */
5367 bytecode_set_operand(bc, j_done, bc->instr_count);
5368 } else if (decl_meta == TYPE_META_NIL) {
5369 /* expect Nil */
5372 int ciNil = bytecode_add_constant(bc, make_string("Nil"));
5375 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5376 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5377 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5378 {
5379 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Nil"));
5383 }
5384 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5385 } else {
5386 /* integer widths: expect Number then range-check */
5387 int abs_bits = decl_bits < 0 ? -decl_bits : decl_bits;
5388 if (abs_bits > 0) {
5389 /* typeof == Number */
5392 int ciNum = bytecode_add_constant(bc, make_string("Number"));
5395 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5396 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5397 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5398 {
5399 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Number"));
5403 }
5404 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5405
5406 if (abs_bits > 0) {
5407 bytecode_add_instruction(bc, (decl_bits < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits);
5408 }
5409 }
5410 }
5411
5412 if (lidx >= 0) {
5414 } else {
5416 }
5417 } else {
5418 /* default initialize if no '=' given */
5419 int ci = -1;
5420 if (is_string) {
5422 } else if (is_nil) {
5424 } else if (is_class) {
5425 /* Class-typed variable defaults to Nil until assigned an instance */
5427 } else if (is_boolean) {
5428 /* booleans default to false */
5429 ci = bytecode_add_constant(bc, make_bool(0));
5430 } else if (is_number || (decl_bits != 0)) {
5431 /* integers default to 0 */
5432 ci = bytecode_add_constant(bc, make_int(0));
5433 }
5434 if (ci >= 0) {
5436 int abs_bits2 = decl_bits < 0 ? -decl_bits : decl_bits;
5437 if (abs_bits2 > 0) {
5438 bytecode_add_instruction(bc, (decl_bits < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits2);
5439 }
5440 if (lidx >= 0) {
5442 } else {
5444 }
5445 }
5446 }
5447 *pos = local_pos;
5448 skip_to_eol(src, len, pos);
5449 return;
5450 }
5451
5452 if (strcmp(name, "print") == 0) {
5453 free(name);
5454 skip_spaces(src, len, &local_pos);
5455 (void)consume_char(src, len, &local_pos, '(');
5456 if (emit_expression(bc, src, len, &local_pos)) {
5457 (void)consume_char(src, len, &local_pos, ')');
5459 } else {
5460 (void)consume_char(src, len, &local_pos, ')');
5461 }
5462 *pos = local_pos;
5463 skip_to_eol(src, len, pos);
5464 return;
5465 }
5466
5467 if (strcmp(name, "echo") == 0) {
5468 free(name);
5469 skip_spaces(src, len, &local_pos);
5470 (void)consume_char(src, len, &local_pos, '(');
5471 if (emit_expression(bc, src, len, &local_pos)) {
5472 (void)consume_char(src, len, &local_pos, ')');
5474 } else {
5475 (void)consume_char(src, len, &local_pos, ')');
5476 }
5477 *pos = local_pos;
5478 skip_to_eol(src, len, pos);
5479 return;
5480 }
5481
5482 /* assignment or simple call */
5483 int lidx = local_find(name);
5484 int gi = (lidx < 0) ? sym_find(name) : -1;
5485 if (lidx < 0 && gi < 0 && g_locals) {
5486 /* Auto-declare as local if we're in a function and it's not known as local or global */
5487 lidx = local_add(name);
5488 }
5489 if (lidx < 0 && gi < 0) {
5490 /* Not local, not existing global -> it's a new global (or a new local if we were in a function but lidx still < 0?)
5491 Actually if g_locals was non-null we already added it to lidx.
5492 If g_locals is NULL, we create it as global. */
5493 gi = sym_index(name);
5494 }
5495 skip_spaces(src, len, &local_pos);
5496
5497 /* object field assignment: supports
5498 - name.field = expr
5499 - name.field[expr] = expr
5500 - name.field[expr1][expr2] = expr
5501 If pattern doesn't match an assignment, fall back to expression stmt (e.g., method call).
5502 */
5503 if (local_pos < len && src[local_pos] == '.') {
5504 size_t stmt_start = *pos; /* for expression fallback */
5505 size_t look = local_pos + 1; /* point after '.' */
5506 skip_spaces(src, len, &look);
5507 char *fname = NULL;
5508 if (!read_identifier_into(src, len, &look, &fname)) {
5509 parser_fail(look, "Expected field name after '.'");
5510 free(name);
5511 return;
5512 }
5513 skip_spaces(src, len, &look);
5514 if (look < len && src[look] == '[') {
5515 /* Handle name.field[...][...] = value */
5516 /* Load base container and resolve field: base[field] -> inner container on stack */
5517 if (lidx >= 0) {
5519 } else {
5521 }
5522 int fci = bytecode_add_constant(bc, make_string(fname));
5523 free(fname);
5526
5527 /* Now parse one or more [expr] */
5528 for (;;) {
5529 if (!(look < len && src[look] == '[')) break;
5530 look++; /* consume '[' */
5531 if (!emit_expression(bc, src, len, &look)) {
5532 parser_fail(look, "Expected index expression after '['");
5533 free(name);
5534 return;
5535 }
5536 if (!consume_char(src, len, &look, ']')) {
5537 parser_fail(look, "Expected ']' after index");
5538 free(name);
5539 return;
5540 }
5541 skip_spaces(src, len, &look);
5542 if (look < len && src[look] == '[') {
5543 /* Need to dereference one level: inner = inner[index] */
5545 continue;
5546 }
5547 break;
5548 }
5549 /* Expect '=' to assign into the last container with last index on stack */
5550 if (look >= len || src[look] != '=') {
5551 /* Not an assignment: fallback to expression statement */
5552 free(name);
5553 size_t expr_pos = stmt_start;
5554 if (emit_expression(bc, src, len, &expr_pos)) {
5556 }
5557 *pos = expr_pos;
5558 skip_to_eol(src, len, pos);
5559 return;
5560 }
5561 look++; /* skip '=' */
5562 if (!emit_expression(bc, src, len, &look)) {
5563 parser_fail(look, "Expected expression after '='");
5564 free(name);
5565 return;
5566 }
5568 free(name);
5569 *pos = look;
5570 skip_to_eol(src, len, pos);
5571 return;
5572 } else if (look < len && src[look] == '=') {
5573 /* Simple name.field = value
5574 * Previous implementation pushed (container, key) first and then evaluated value.
5575 * That relies on CALL preserving underlying stack entries. To be robust,
5576 * we first evaluate the value into a temporary local, then push (container, key),
5577 * then load the temporary and perform INDEX_SET.
5578 */
5579
5580 /* Advance to after '=' and parse value into a temporary local */
5581 local_pos = look + 1;
5582 if (!emit_expression(bc, src, len, &local_pos)) {
5583 parser_fail(local_pos, "Expected expression after '='");
5584 free(fname);
5585 free(name);
5586 return;
5587 }
5588 /* store value to a hidden temp local */
5589 int tmp_local = local_add("__assign_tmp");
5591
5592 /* Load container variable */
5593 if (lidx >= 0) {
5595 } else {
5597 }
5598 /* Push key */
5599 int kci = bytecode_add_constant(bc, make_string(fname));
5600 free(fname);
5602
5603 /* Load value from temp and set */
5606
5607 free(name);
5608 *pos = local_pos;
5609 skip_to_eol(src, len, pos);
5610 return;
5611 } else {
5612 /* Not an assignment: treat as expression statement (e.g., obj.method(...)) */
5613 free(fname);
5614 free(name);
5615 size_t expr_pos = stmt_start;
5616 if (emit_expression(bc, src, len, &expr_pos)) {
5618 }
5619 *pos = expr_pos;
5620 skip_to_eol(src, len, pos);
5621 return;
5622 }
5623 }
5624
5625 /* array element assignment: name[expr] = expr and nested: name[expr1][expr2] = expr */
5626 if (local_pos < len && src[local_pos] == '[') {
5627 /* load array/map variable */
5628 if (lidx >= 0) {
5630 } else {
5632 }
5633 local_pos++; /* '[' */
5634 if (!emit_expression(bc, src, len, &local_pos)) {
5635 parser_fail(local_pos, "Expected index expression after '['");
5636 free(name);
5637 return;
5638 }
5639 if (!consume_char(src, len, &local_pos, ']')) {
5640 parser_fail(local_pos, "Expected ']' after index");
5641 free(name);
5642 return;
5643 }
5644 skip_spaces(src, len, &local_pos);
5645
5646 /* Nested index: name[expr1][expr2] = value */
5647 if (local_pos < len && src[local_pos] == '[') {
5648 /* Reduce base: stack currently has container, index1 -> get inner container */
5650
5651 local_pos++; /* second '[' */
5652 if (!emit_expression(bc, src, len, &local_pos)) {
5653 parser_fail(local_pos, "Expected nested index expression after '['");
5654 free(name);
5655 return;
5656 }
5657 if (!consume_char(src, len, &local_pos, ']')) {
5658 parser_fail(local_pos, "Expected ']' after nested index");
5659 free(name);
5660 return;
5661 }
5662 skip_spaces(src, len, &local_pos);
5663 if (local_pos >= len || src[local_pos] != '=') {
5664 parser_fail(local_pos, "Expected '=' after nested array index");
5665 free(name);
5666 return;
5667 }
5668 local_pos++; /* '=' */
5669 if (!emit_expression(bc, src, len, &local_pos)) {
5670 parser_fail(local_pos, "Expected expression after '='");
5671 free(name);
5672 return;
5673 }
5674 /* perform set into inner container */
5676 free(name);
5677 *pos = local_pos;
5678 skip_to_eol(src, len, pos);
5679 return;
5680 }
5681
5682 /* Single-level: name[expr] = value */
5683 if (local_pos >= len || src[local_pos] != '=') {
5684 parser_fail(local_pos, "Expected '=' after array index");
5685 free(name);
5686 return;
5687 }
5688 local_pos++; /* '=' */
5689 if (!emit_expression(bc, src, len, &local_pos)) {
5690 parser_fail(local_pos, "Expected expression after '='");
5691 free(name);
5692 return;
5693 }
5694 /* perform set */
5696 free(name);
5697 *pos = local_pos;
5698 skip_to_eol(src, len, pos);
5699 return;
5700 }
5701
5702 free(name);
5703 if (local_pos < len && src[local_pos] == '=') {
5704 local_pos++; /* '=' */
5705 if (emit_expression(bc, src, len, &local_pos)) {
5706 /* enforce declared type if present (0 = dynamic) */
5707 int meta = 0;
5708 if (lidx >= 0 && g_locals) {
5709 meta = g_locals->types[lidx];
5710 } else if (gi >= 0) {
5711 meta = G.types[gi];
5712 }
5713
5714 if (meta == TYPE_META_STRING) {
5715 /* expect String */
5718 int ciStr = bytecode_add_constant(bc, make_string("String"));
5721 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5722 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5723 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5724 {
5725 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected String"));
5729 }
5730 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5731 } else if (meta == TYPE_META_CLASS) {
5732 /* expect Class instance: Map with "__class" key */
5733 /* Check typeof(v) == "Map" */
5736 {
5737 int ciMap = bytecode_add_constant(bc, make_string("Map"));
5739 }
5741 int j_err1 = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5742 /* Check v has "__class" */
5744 {
5745 int kci = bytecode_add_constant(bc, make_string("__class"));
5747 }
5749 int j_err2 = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5750 /* Success path jumps over error block */
5751 int j_ok = bytecode_add_instruction(bc, OP_JUMP, 0);
5752 /* Error block */
5753 int err_lbl = bc->instr_count;
5754 bytecode_set_operand(bc, j_err1, err_lbl);
5755 bytecode_set_operand(bc, j_err2, err_lbl);
5756 {
5757 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Class"));
5761 }
5762 /* Continue after checks */
5763 bytecode_set_operand(bc, j_ok, bc->instr_count);
5764 } else if (meta == TYPE_META_FLOAT) {
5765 /* expect Float */
5768 int ciF = bytecode_add_constant(bc, make_string("Float"));
5771 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5772 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5773 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5774 {
5775 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Float"));
5779 }
5780 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5781 } else if (meta == TYPE_META_ARRAY) {
5782 /* expect Array */
5785 int ciArr = bytecode_add_constant(bc, make_string("Array"));
5788 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5789 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5790 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5791 {
5792 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Array"));
5796 }
5797 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5798 } else if (meta == TYPE_META_BOOLEAN) {
5799 /* expect Number then clamp to 1 bit (unsigned) */
5802 int ciNum = bytecode_add_constant(bc, make_string("Number"));
5805 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5806 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5807 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5808 {
5809 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Number for boolean"));
5813 }
5814 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5816 } else if (meta == TYPE_META_NIL) {
5817 /* expect Nil */
5820 int ciNil = bytecode_add_constant(bc, make_string("Nil"));
5823 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5824 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5825 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5826 {
5827 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Nil"));
5831 }
5832 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5833 } else if (meta != 0) {
5834 /* integer widths: expect Number then range-check to declared width */
5835 int abs_bits = meta < 0 ? -meta : meta;
5836 /* typeof == Number */
5839 int ciNum = bytecode_add_constant(bc, make_string("Number"));
5842 int j_to_error = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
5843 int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
5844 bytecode_set_operand(bc, j_to_error, bc->instr_count);
5845 {
5846 int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Number"));
5850 }
5851 bytecode_set_operand(bc, j_skip_err, bc->instr_count);
5852
5853 if (abs_bits > 0) {
5854 bytecode_add_instruction(bc, (meta < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits);
5855 }
5856 }
5857 /* dynamic (meta==0): no enforcement */
5858
5859 if (lidx >= 0) {
5861 } else {
5863 }
5864 }
5865 *pos = local_pos;
5866 skip_to_eol(src, len, pos);
5867 return;
5868 } else {
5869 /* Treat as a generic expression-statement (e.g., method calls like obj.method(...),
5870 property access chains, or builtin calls behind wrappers). We rewind to the
5871 start of the statement, emit the full expression, and drop its result. */
5872 local_pos = *pos;
5873 if (emit_expression(bc, src, len, &local_pos)) {
5874 bytecode_add_instruction(bc, OP_POP, 0); /* discard return value of standalone expr */
5875 *pos = local_pos;
5876 skip_to_eol(src, len, pos);
5877 return;
5878 }
5879 /* if it wasn't a valid expression either, report a meaningful error */
5880 parser_fail(local_pos, "Expected assignment '=' or call '(...)' after identifier");
5881 return;
5882 }
5883 }
5884
5885 /* fallback: print(expr) without identifier read (unlikely) */
5886 if (starts_with(src, len, *pos, "print")) {
5887 *pos += 5;
5888 skip_spaces(src, len, pos);
5889 (void)consume_char(src, len, pos, '(');
5890 if (emit_expression(bc, src, len, pos)) {
5891 (void)consume_char(src, len, pos, ')');
5893 } else {
5894 (void)consume_char(src, len, pos, ')');
5895 }
5896 skip_to_eol(src, len, pos);
5897 return;
5898 }
5899
5900 /* echo(expr): like print but does not add a newline (immediate output) */
5901 if (starts_with(src, len, *pos, "echo")) {
5902 *pos += 4;
5903 skip_spaces(src, len, pos);
5904 (void)consume_char(src, len, pos, '(');
5905 if (emit_expression(bc, src, len, pos)) {
5906 (void)consume_char(src, len, pos, ')');
5908 } else {
5909 (void)consume_char(src, len, pos, ')');
5910 }
5911 skip_to_eol(src, len, pos);
5912 return;
5913 }
5914
5915 /* unknown token: report error */
5916 parser_fail(*pos, "Unknown token at start of statement");
5917}
5918
5919/* parse a block with lines at indentation >= current_indent; stop at dedent */
5933static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos, int current_indent) {
5934 while (*pos < len) {
5935 if (g_has_error) return;
5936 size_t line_start = *pos; /* remember raw line start for dedent backtracking */
5937 int indent = 0;
5938 if (!read_line_start(src, len, pos, &indent)) {
5939 /* EOF or error */
5940 return;
5941 }
5942 if (indent < current_indent) {
5943 /* dedent -> let caller handle this line */
5944 *pos = line_start;
5945 return;
5946 }
5947 if (indent > current_indent) {
5948 /* nested block without a header (tolerate by parsing it and continuing) */
5949 parse_block(bc, src, len, pos, indent);
5950 continue;
5951 }
5952
5953 /* at same indent -> parse statement */
5954 /* Insert a line marker for better runtime error reporting. Use the current
5955 * position (start of the actual statement) returned by read_line_start(),
5956 * not the pre-scan position which may point to a comment/blank line. */
5957 {
5958 int stmt_line = 1, stmt_col = 1;
5959 calc_line_col(src, len, *pos, &stmt_line, &stmt_col);
5960 bytecode_add_instruction(bc, OP_LINE, stmt_line);
5961 }
5962
5963 /* class definition -> factory function */
5964 if (starts_with(src, len, *pos, "class") && (*pos + 5 < len) && (src[*pos + 5] == ' ' || src[*pos + 5] == '\t')) {
5965 *pos += 5;
5966 skip_spaces(src, len, pos);
5967 /* class name */
5968 char *cname = NULL;
5969 if (!read_identifier_into(src, len, pos, &cname)) {
5970 parser_fail(*pos, "Expected class name after 'class'");
5971 return;
5972 }
5973 int cgi = sym_index(cname);
5974
5975 /* optional extends Parent */
5976 char *parent_name = NULL;
5977
5978 /* Optional typed parameter list: class Name(type ident, ...) */
5979 char *param_names[64];
5980 int param_kind[64];
5981 int pcount = 0;
5982 memset(param_names, 0, sizeof(param_names));
5983 memset(param_kind, 0, sizeof(param_kind));
5984
5985/* kind: 1=Number (numeric types incl. boolean), 2=String, 3=Nil */
5986/* helper macro instead of nested function (C99 compliant) */
5987#define MAP_TYPE_KIND(t) ( \
5988 ((t) && strcmp((t), "string") == 0) ? 2 : ((t) && strcmp((t), "nil") == 0) ? 3 \
5989 : ((t) && (strcmp((t), "boolean") == 0 || strcmp((t), "number") == 0 || strcmp((t), "byte") == 0 || strncmp((t), "uint", 4) == 0 || strncmp((t), "sint", 4) == 0 || strncmp((t), "int", 3) == 0)) ? 1 \
5990 : 0)
5991
5992 skip_spaces(src, len, pos);
5993 if (*pos < len && src[*pos] == '(') {
5994 (*pos)++;
5995 skip_spaces(src, len, pos);
5996 if (*pos < len && src[*pos] != ')') {
5997 for (;;) {
5998 /* read type token */
5999 char *tname = NULL;
6000 if (!read_identifier_into(src, len, pos, &tname)) {
6001 parser_fail(*pos, "Expected type in class parameter list");
6002 free(cname);
6003 return;
6004 }
6005 /* read param name */
6006 skip_spaces(src, len, pos);
6007 char *pname = NULL;
6008 if (!read_identifier_into(src, len, pos, &pname)) {
6009 parser_fail(*pos, "Expected parameter name after type");
6010 free(tname);
6011 free(cname);
6012 return;
6013 }
6014 if (pcount >= (int)(sizeof(param_names) / sizeof(param_names[0]))) {
6015 parser_fail(*pos, "Too many class parameters");
6016 free(tname);
6017 free(pname);
6018 free(cname);
6019 return;
6020 }
6021 param_names[pcount] = pname;
6022 param_kind[pcount] = MAP_TYPE_KIND(tname);
6023 free(tname);
6024 pcount++;
6025
6026 skip_spaces(src, len, pos);
6027 if (*pos < len && src[*pos] == ',') {
6028 (*pos)++;
6029 skip_spaces(src, len, pos);
6030 continue;
6031 }
6032 break;
6033 }
6034 }
6035 if (!consume_char(src, len, pos, ')')) {
6036 parser_fail(*pos, "Expected ')' after class parameter list");
6037 for (int i = 0; i < pcount; ++i)
6038 free(param_names[i]);
6039 free(cname);
6040 return;
6041 }
6042 }
6043
6044 /* optional 'extends Parent' after parameter list */
6045 skip_spaces(src, len, pos);
6046 if (starts_with(src, len, *pos, "extends")) {
6047 *pos += 7; /* consume 'extends' */
6048 skip_spaces(src, len, pos);
6049 if (!read_identifier_into(src, len, pos, &parent_name)) {
6050 parser_fail(*pos, "Expected parent class name after 'extends'");
6051 for (int i = 0; i < pcount; ++i)
6052 free(param_names[i]);
6053 free(cname);
6054 return;
6055 }
6056 }
6057
6058 /* end of class header line */
6059 skip_to_eol(src, len, pos);
6060
6061 /* Build factory function: Name(...) -> instance map with fields and methods */
6062 Bytecode *ctor_bc = bytecode_new();
6063 /* set debug metadata for class factory */
6064 if (ctor_bc) {
6065 if (ctor_bc->name) free((void *)ctor_bc->name);
6066 ctor_bc->name = strdup(cname);
6067 if (ctor_bc->source_file) free((void *)ctor_bc->source_file);
6068 if (g_current_source_path) ctor_bc->source_file = strdup(g_current_source_path);
6069 }
6070 /* local env for the factory to allow temp locals */
6071 LocalEnv ctor_env;
6072 memset(&ctor_env, 0, sizeof(ctor_env));
6073 LocalEnv *prev_env = g_locals;
6074 g_locals = &ctor_env;
6075
6076 /* track if _construct is defined in this class */
6077 int ctor_present = 0;
6078
6079 /* Register parameter locals first so args land at 0..pcount-1 */
6080 for (int i = 0; i < pcount; ++i) {
6081 local_add(param_names[i]);
6082 }
6083
6084 /* Guard local to detect extra argument at index == pcount */
6085 int l_extra = local_add("__extra");
6086
6087 /* Runtime checks: missing args and type checks */
6088 for (int i = 0; i < pcount; ++i) {
6089 /* missing arg: local i must not be Nil */
6092 int ci_nil = bytecode_add_constant(ctor_bc, make_string("Nil"));
6093 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_nil);
6094 bytecode_add_instruction(ctor_bc, OP_EQ, 0);
6095 int j_ok_present = bytecode_add_instruction(ctor_bc, OP_JUMP_IF_FALSE, 0);
6096 /* then -> error */
6097 {
6098 char msg[128];
6099 snprintf(msg, sizeof(msg), "TypeError: missing argument '%s' in %s()", param_names[i], cname);
6100 int ci_msg = bytecode_add_constant(ctor_bc, make_string(msg));
6101 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_msg);
6104 }
6105 /* patch continue if present */
6106 bytecode_set_operand(ctor_bc, j_ok_present, ctor_bc->instr_count);
6107
6108 /* type check for known kinds */
6109 int kind = param_kind[i];
6110 if (kind == 1 || kind == 2 || kind == 3) {
6111 /* typeof(local i) == Expected ? skip error : go to error */
6114 const char *exp = (kind == 1) ? "Number" : (kind == 2) ? "String"
6115 : "Nil";
6116 int ci_exp = bytecode_add_constant(ctor_bc, make_string(exp));
6117 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_exp);
6118 bytecode_add_instruction(ctor_bc, OP_EQ, 0);
6119 /* if false -> jump to error */
6120 int j_to_error = bytecode_add_instruction(ctor_bc, OP_JUMP_IF_FALSE, 0);
6121 /* on success, skip error block */
6122 int j_skip_err = bytecode_add_instruction(ctor_bc, OP_JUMP, 0);
6123 /* error block */
6124 {
6125 int err_label = ctor_bc->instr_count;
6126 bytecode_set_operand(ctor_bc, j_to_error, err_label);
6127 char msg2[160];
6128 snprintf(msg2, sizeof(msg2), "TypeError: %s() expects %s for '%s'", cname, exp, param_names[i]);
6129 int ci_msg2 = bytecode_add_constant(ctor_bc, make_string(msg2));
6130 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_msg2);
6133 }
6134 /* continue label after error block */
6135 bytecode_set_operand(ctor_bc, j_skip_err, ctor_bc->instr_count);
6136 }
6137 }
6138
6139 /* Extra args check: guard local must be Nil; if not Nil -> error */
6140 {
6141 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_extra);
6143 int ci_nil2 = bytecode_add_constant(ctor_bc, make_string("Nil"));
6144 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_nil2);
6145 bytecode_add_instruction(ctor_bc, OP_EQ, 0);
6146 /* if false (not Nil) -> jump to error */
6147 int j_to_error = bytecode_add_instruction(ctor_bc, OP_JUMP_IF_FALSE, 0);
6148 /* if true (Nil) -> skip error */
6149 int j_skip_err = bytecode_add_instruction(ctor_bc, OP_JUMP, 0);
6150 {
6151 int err_label = ctor_bc->instr_count;
6152 bytecode_set_operand(ctor_bc, j_to_error, err_label);
6153 char msg3[128];
6154 snprintf(msg3, sizeof(msg3), "TypeError: %s() received too many arguments", cname);
6155 int ci_msg3 = bytecode_add_constant(ctor_bc, make_string(msg3));
6156 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_msg3);
6159 }
6160 bytecode_set_operand(ctor_bc, j_skip_err, ctor_bc->instr_count);
6161 }
6162
6163 /* instance map: __this = {} (placed after param guard) */
6164 int l_this = local_add("__this");
6167
6168 /* tag instance with its class name: this["__class"] = "<ClassName>" */
6169 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
6170 {
6171 int kci_cls = bytecode_add_constant(ctor_bc, make_string("__class"));
6172 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, kci_cls);
6173 int vci_cls = bytecode_add_constant(ctor_bc, make_string(cname));
6174 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, vci_cls);
6175 }
6177
6178 /* Inheritance: if extends Parent, create Parent(header args...) and merge its keys into this */
6179 if (parent_name) {
6180 /* parent_inst = Parent(args...) */
6181 int parent_gi = sym_index(parent_name);
6182 bytecode_add_instruction(ctor_bc, OP_LOAD_GLOBAL, parent_gi);
6183 for (int i = 0; i < pcount; ++i) {
6185 }
6186 bytecode_add_instruction(ctor_bc, OP_CALL, pcount);
6187 int l_parent = local_add("__parent_inst");
6188 bytecode_add_instruction(ctor_bc, OP_STORE_LOCAL, l_parent);
6189
6190 /* keys = keys(parent_inst) */
6191 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_parent);
6193 int l_keys = local_add("__parent_keys");
6195
6196 /* i = 0 */
6197 int c0_inh = bytecode_add_constant(ctor_bc, make_int(0));
6198 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, c0_inh);
6199 int l_i = local_add("__inh_i");
6201
6202 /* loop: while (i < len(keys)) */
6203 int loop_start = ctor_bc->instr_count;
6205 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_keys);
6206 bytecode_add_instruction(ctor_bc, OP_LEN, 0);
6207 bytecode_add_instruction(ctor_bc, OP_LT, 0);
6208 int jmp_false = bytecode_add_instruction(ctor_bc, OP_JUMP_IF_FALSE, 0);
6209
6210 /* key = keys[i] */
6211 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_keys);
6214 int l_k = local_add("__inh_k");
6216
6217 /* if this has key -> skip set */
6218 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
6221 int j_skip_set = bytecode_add_instruction(ctor_bc, OP_JUMP_IF_FALSE, 0);
6222 /* has key -> nothing to do, jump over set sequence */
6223 int j_after_maybe_set = bytecode_add_instruction(ctor_bc, OP_JUMP, 0);
6224
6225 /* not has key: set this[key] = parent_inst[key] */
6226 bytecode_set_operand(ctor_bc, j_skip_set, ctor_bc->instr_count);
6227 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
6229 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_parent);
6233
6234 /* continue after maybe-set */
6235 bytecode_set_operand(ctor_bc, j_after_maybe_set, ctor_bc->instr_count);
6236
6237 /* i++ */
6238 int c1_inh = bytecode_add_constant(ctor_bc, make_int(1));
6240 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, c1_inh);
6241 bytecode_add_instruction(ctor_bc, OP_ADD, 0);
6243
6244 /* back to loop */
6245 bytecode_add_instruction(ctor_bc, OP_JUMP, loop_start);
6246 /* end loop */
6247 bytecode_set_operand(ctor_bc, jmp_false, ctor_bc->instr_count);
6248 }
6249
6250 /* Parse class body at increased indent */
6251 int body_indent = 0;
6252 size_t look_body = *pos;
6253 if (read_line_start(src, len, &look_body, &body_indent) && body_indent > current_indent) {
6254 /* iterate over class members at body_indent */
6255 for (;;) {
6256 size_t member_line_start = *pos;
6257 int member_indent = 0;
6258 if (!read_line_start(src, len, pos, &member_indent)) {
6259 /* EOF */
6260 break;
6261 }
6262 if (member_indent < body_indent) {
6263 /* end of class body */
6264 *pos = member_line_start;
6265 break;
6266 }
6267 if (member_indent > body_indent) {
6268 /* skip nested blocks that are part of previous method parsing */
6269 parse_block(ctor_bc, src, len, pos, member_indent);
6270 continue;
6271 }
6272
6273 /* at body_indent: member declaration (field = expr) or method 'fun name(...)' */
6274 if (starts_with(src, len, *pos, "fun")) {
6275 /* method definition: fun m(this, ...) ... */
6276 *pos += 3;
6277 skip_spaces(src, len, pos);
6278 char *mname = NULL;
6279 if (!read_identifier_into(src, len, pos, &mname)) {
6280 parser_fail(*pos, "Expected method name after 'fun' in class");
6281 g_locals = prev_env;
6282 free(cname);
6283 return;
6284 }
6285 int is_ctor_method = (strcmp(mname, "_construct") == 0);
6286
6287 skip_spaces(src, len, pos);
6288 if (!consume_char(src, len, pos, '(')) {
6289 parser_fail(*pos, "Expected '(' after method name");
6290 free(mname);
6291 g_locals = prev_env;
6292 free(cname);
6293 return;
6294 }
6295
6296 /* Build method function bytecode */
6297 Bytecode *m_bc = bytecode_new();
6298 if (m_bc) {
6299 if (m_bc->name) free((void *)m_bc->name);
6300 /* method qualified name: Class.method */
6301 size_t qlen = strlen(cname) + 1 + strlen(mname) + 1;
6302 char *q = (char *)malloc(qlen);
6303 if (q) {
6304 snprintf(q, qlen, "%s.%s", cname, mname);
6305 m_bc->name = q;
6306 }
6307 if (m_bc->source_file) free((void *)m_bc->source_file);
6308 if (g_current_source_path) m_bc->source_file = strdup(g_current_source_path);
6309 }
6310 LocalEnv m_env;
6311 memset(&m_env, 0, sizeof(m_env));
6312 LocalEnv *saved = g_locals;
6313 g_locals = &m_env;
6314
6315 /* Parse params, ensure first is 'this' (insert if missing) */
6316 int saw_param = 0;
6317 int param_count = 0;
6318 skip_spaces(src, len, pos);
6319 if (*pos < len && src[*pos] != ')') {
6320 for (;;) {
6321 char *pname = NULL;
6322 if (!read_identifier_into(src, len, pos, &pname)) {
6323 parser_fail(*pos, "Expected parameter name");
6324 free(mname);
6325 g_locals = saved;
6326 g_locals = prev_env;
6327 free(cname);
6328 return;
6329 }
6330 if (param_count == 0 && strcmp(pname, "this") != 0) {
6331 /* Require explicit 'this' as first parameter */
6332 if (is_ctor_method) {
6333 parser_fail(*pos, "Constructor '_construct' must declare 'this' as its first parameter");
6334 } else {
6335 parser_fail(*pos, "First parameter of a method must be 'this'");
6336 }
6337 free(pname);
6338 free(mname);
6339 g_locals = saved;
6340 g_locals = prev_env;
6341 free(cname);
6342 return;
6343 }
6344 local_add(pname);
6345 free(pname);
6346 param_count++;
6347 skip_spaces(src, len, pos);
6348 if (*pos < len && src[*pos] == ',') {
6349 (*pos)++;
6350 skip_spaces(src, len, pos);
6351 continue;
6352 }
6353 break;
6354 }
6355 } else {
6356 /* no params: enforce (this) */
6357 if (is_ctor_method) {
6358 parser_fail(*pos, "Constructor '_construct' must declare 'this' as its first parameter");
6359 } else {
6360 parser_fail(*pos, "Method must declare at least 'this' parameter");
6361 }
6362 free(mname);
6363 g_locals = saved;
6364 g_locals = prev_env;
6365 free(cname);
6366 return;
6367 }
6368
6369 if (!consume_char(src, len, pos, ')')) {
6370 parser_fail(*pos, "Expected ')' after method parameter list");
6371 free(mname);
6372 g_locals = saved;
6373 g_locals = prev_env;
6374 free(cname);
6375 return;
6376 }
6377 /* end header line */
6378 skip_to_eol(src, len, pos);
6379
6380 /* parse method body at increased indent */
6381 int m_body_indent = 0;
6382 size_t look_m = *pos;
6383 if (read_line_start(src, len, &look_m, &m_body_indent) && m_body_indent > body_indent) {
6384 parse_block(m_bc, src, len, pos, m_body_indent);
6385 } else {
6386 /* empty method body allowed -> return */
6387 }
6388 /* ensure return */
6390
6391 /* restore env to factory */
6392 g_locals = saved;
6393
6394 /* Insert method function into instance: this["mname"] = <function> */
6395 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
6396 int kci = bytecode_add_constant(ctor_bc, make_string(mname));
6398 int mci = bytecode_add_constant(ctor_bc, make_function(m_bc));
6401
6402 /* mark constructor presence if name matches */
6403 if (strcmp(mname, "_construct") == 0) {
6404 ctor_present = 1;
6405 }
6406
6407 free(mname);
6408 continue;
6409 }
6410
6411 /* field initializer: ident = expr */
6412 size_t lp = *pos;
6413 char *fname = NULL;
6414 if (!read_identifier_into(src, len, &lp, &fname)) {
6415 parser_fail(*pos, "Expected field or 'fun' in class body");
6416 g_locals = prev_env;
6417 free(cname);
6418 return;
6419 }
6420 size_t tmp = lp;
6421 skip_spaces(src, len, &tmp);
6422 if (tmp >= len || src[tmp] != '=') {
6423 free(fname);
6424 parser_fail(tmp, "Expected '=' in field initializer");
6425 g_locals = prev_env;
6426 free(cname);
6427 return;
6428 }
6429 /* commit position and consume '=' */
6430 *pos = tmp + 1;
6431 /* emit: this["fname"] = (expr) */
6432 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
6433 int fkey = bytecode_add_constant(ctor_bc, make_string(fname));
6435 free(fname);
6436 if (!emit_expression(ctor_bc, src, len, pos)) {
6437 parser_fail(*pos, "Expected expression in field initializer");
6438 g_locals = prev_env;
6439 free(cname);
6440 return;
6441 }
6443 /* end of line */
6444 skip_to_eol(src, len, pos);
6445 }
6446 } else {
6447 /* empty class body allowed */
6448 }
6449
6450 /* Override defaults with constructor parameters: this["name"] = local i */
6451 for (int i = 0; i < pcount; ++i) {
6452 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
6453 int kci = bytecode_add_constant(ctor_bc, make_string(param_names[i]));
6457 }
6458
6459 /* If a constructor exists, invoke: this._construct(this, params...) and drop its return */
6460 if (ctor_present) {
6461 /* fetch method: duplicate 'this' so we keep it for the call */
6462 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
6463 bytecode_add_instruction(ctor_bc, OP_DUP, 0); /* -> this, this */
6464 {
6465 int kci_ctor = bytecode_add_constant(ctor_bc, make_string("_construct"));
6466 bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, kci_ctor);
6467 }
6468 bytecode_add_instruction(ctor_bc, OP_INDEX_GET, 0); /* -> this, func */
6469 bytecode_add_instruction(ctor_bc, OP_SWAP, 0); /* -> func, this */
6470
6471 /* push all header parameters as additional args in order */
6472 for (int i = 0; i < pcount; ++i) {
6474 }
6475 /* call with implicit 'this' (+1) plus pcount params */
6476 bytecode_add_instruction(ctor_bc, OP_CALL, pcount + 1);
6477 /* discard any return value from constructor */
6478 bytecode_add_instruction(ctor_bc, OP_POP, 0);
6479 }
6480
6481 /* return instance */
6482 bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
6484
6485 /* restore outer locals env */
6486 g_locals = prev_env;
6487
6488 /* bind factory function globally under class name */
6489 int cci = bytecode_add_constant(bc, make_function(ctor_bc));
6492 /* mark this global as a class for typeof(identifier) */
6493 G.is_class[cgi] = 1;
6494
6495 for (int i = 0; i < pcount; ++i)
6496 free(param_names[i]);
6497 if (parent_name) free(parent_name);
6498 free(cname);
6499 continue;
6500 }
6501
6502 if (starts_with(src, len, *pos, "fun") && (*pos + 3 < len) && (src[*pos + 3] == ' ' || src[*pos + 3] == '\t')) {
6503 /* parse header: fun name(arg, ...) */
6504 *pos += 3;
6505 skip_spaces(src, len, pos);
6506 char *fname = NULL;
6507 if (!read_identifier_into(src, len, pos, &fname)) {
6508 parser_fail(*pos, "Expected function name after 'fun'");
6509 return;
6510 }
6511 int fgi = sym_index(fname);
6512 skip_spaces(src, len, pos);
6513 if (!consume_char(src, len, pos, '(')) {
6514 parser_fail(*pos, "Expected '(' after function name");
6515 free(fname);
6516 return;
6517 }
6518
6519 /* build locals from parameters */
6520 LocalEnv env = {{0}, {0}, 0};
6521 LocalEnv *prev = g_locals;
6522 int saved_depth = g_func_env_depth;
6523 if (prev != NULL) {
6524 if (g_func_env_depth >= (int)(sizeof(g_func_env_stack) / sizeof(g_func_env_stack[0]))) {
6525 parser_fail(*pos, "Too many nested functions (env stack overflow)");
6526 free(fname);
6527 return;
6528 }
6529 g_func_env_stack[g_func_env_depth++] = prev;
6530 }
6531 g_locals = &env;
6532
6533 skip_spaces(src, len, pos);
6534 if (*pos < len && src[*pos] != ')') {
6535 for (;;) {
6536 char *pname = NULL;
6537 if (!read_identifier_into(src, len, pos, &pname)) {
6538 parser_fail(*pos, "Expected parameter name");
6539 g_locals = prev;
6540 free(fname);
6541 return;
6542 }
6543 if (local_find(pname) >= 0) {
6544 parser_fail(*pos, "Duplicate parameter name '%s'", pname);
6545 free(pname);
6546 g_locals = prev;
6547 free(fname);
6548 return;
6549 }
6550 local_add(pname);
6551 free(pname);
6552 skip_spaces(src, len, pos);
6553 if (*pos < len && src[*pos] == ',') {
6554 (*pos)++;
6555 skip_spaces(src, len, pos);
6556 continue;
6557 }
6558 break;
6559 }
6560 }
6561 if (!consume_char(src, len, pos, ')')) {
6562 parser_fail(*pos, "Expected ')' after parameter list");
6563 g_locals = prev;
6564 free(fname);
6565 return;
6566 }
6567 /* end header line */
6568 skip_to_eol(src, len, pos);
6569
6570 /* compile body into separate Bytecode */
6571 Bytecode *fn_bc = bytecode_new();
6572 if (fn_bc) {
6573 if (fn_bc->name) free((void *)fn_bc->name);
6574 fn_bc->name = strdup(fname);
6575 if (fn_bc->source_file) free((void *)fn_bc->source_file);
6576 if (g_current_source_path) fn_bc->source_file = strdup(g_current_source_path);
6577 }
6578
6579 /* parse body at increased indent if present */
6580 int body_indent = 0;
6581 size_t look_body = *pos;
6582 if (read_line_start(src, len, &look_body, &body_indent) && body_indent > current_indent) {
6583 /* do not advance pos here; let parse_block consume the line */
6584 parse_block(fn_bc, src, len, pos, body_indent);
6585 } else {
6586 /* empty body: ok */
6587 }
6588 /* ensure function returns */
6590
6591#ifdef FUN_DEBUG
6592 /* DEBUG: dump compiled function bytecode (guarded by runtime env) */
6593 if (fun_debug_enabled()) {
6594 printf("=== compiled function %s (%d params) ===\n", fname, env.count);
6595 bytecode_dump(fn_bc);
6596 printf("=== end function %s ===\n", fname);
6597 }
6598#endif
6599
6600 /* If we are inside another function (prev != NULL), bind this function as a local
6601 * so that it is only visible within the current function scope. Otherwise, bind
6602 * it as a global (top-level behavior unchanged). */
6603 int fci = bytecode_add_constant(bc, make_function(fn_bc));
6605 if (prev != NULL) {
6606 /* Bind into the OUTER function's local env (prev), not the inner temp env. */
6607 LocalEnv *save_env = g_locals;
6608 g_locals = prev;
6609 /* declare a local with the function name if not present */
6610 int lidx = local_find(fname);
6611 if (lidx < 0) {
6612 lidx = local_add(fname);
6613 }
6614 if (lidx < 0) {
6615 /* failed to allocate local slot */
6616 g_locals = save_env; /* restore before returning */
6617 g_locals = prev;
6618 free(fname);
6619 return;
6620 }
6621 /* We want sibling inner functions to be able to call this nested function
6622 * without closures. Strategy: also expose it as a global symbol so that
6623 * inner functions (compiled into separate frames) can resolve the name
6624 * via global lookup. Keep the local binding for the outer function body. */
6625 /* Duplicate the function value so we can store to both local and global. */
6628 /* Bind a global under the same name for call resolution inside deeper nested functions. */
6629 int gsym = sym_index(fname);
6631 /* restore current env (will be reset to prev below) */
6632 g_locals = save_env;
6633 } else {
6634 /* top-level: global binding (backward compatible) */
6636 }
6637
6638 g_locals = prev;
6639 g_func_env_depth = saved_depth;
6640 free(fname);
6641 continue;
6642 }
6643
6644 /* for-sugar:
6645 * - for <ident> in range(a, b)
6646 * - for <ident> in <array-expr>
6647 * - for (<keyIdent>, <valIdent>) in <map-expr>
6648 */
6649 if (starts_with(src, len, *pos, "for")) {
6650 *pos += 3;
6651 skip_spaces(src, len, pos);
6652
6653 /* Optional tuple '(k, v)' for map iteration */
6654 int tuple_mode = 0; /* 0 = single var, 1 = (k,v) */
6655 char *ivar = NULL; /* single variable name OR key variable when tuple */
6656 char *vvar = NULL; /* value variable when tuple */
6657
6658 if (*pos < len && src[*pos] == '(') {
6659 /* Parse '(k, v)' */
6660 (*pos)++;
6661 skip_spaces(src, len, pos);
6662 if (!read_identifier_into(src, len, pos, &ivar)) {
6663 parser_fail(*pos, "Expected identifier after '(' in for tuple");
6664 return;
6665 }
6666 skip_spaces(src, len, pos);
6667 if (!consume_char(src, len, pos, ',')) {
6668 parser_fail(*pos, "Expected ',' between key and value in for tuple");
6669 free(ivar);
6670 return;
6671 }
6672 skip_spaces(src, len, pos);
6673 if (!read_identifier_into(src, len, pos, &vvar)) {
6674 parser_fail(*pos, "Expected value identifier after ',' in for tuple");
6675 free(ivar);
6676 return;
6677 }
6678 skip_spaces(src, len, pos);
6679 if (!consume_char(src, len, pos, ')')) {
6680 parser_fail(*pos, "Expected ')' to close for tuple");
6681 free(ivar);
6682 free(vvar);
6683 return;
6684 }
6685 tuple_mode = 1;
6686 } else {
6687 /* loop variable name */
6688 if (!read_identifier_into(src, len, pos, &ivar)) {
6689 parser_fail(*pos, "Expected loop variable after 'for'");
6690 return;
6691 }
6692 }
6693
6694 skip_spaces(src, len, pos);
6695 if (!starts_with(src, len, *pos, "in")) {
6696 parser_fail(*pos, "Expected 'in' after loop variable");
6697 free(ivar);
6698 return;
6699 }
6700 *pos += 2;
6701 skip_spaces(src, len, pos);
6702
6703 if (starts_with(src, len, *pos, "range")) {
6704 /* ===== range(a, b) variant ===== */
6705 *pos += 5;
6706 if (!consume_char(src, len, pos, '(')) {
6707 parser_fail(*pos, "Expected '(' after range");
6708 free(ivar);
6709 return;
6710 }
6711
6712 /* Parse start expression */
6713 if (!emit_expression(bc, src, len, pos)) {
6714 parser_fail(*pos, "Expected start expression in range");
6715 free(ivar);
6716 return;
6717 }
6718
6719 /* Determine loop variable storage and store start value */
6720 int lidx = local_find(ivar);
6721 int gi = -1;
6722 if (lidx < 0) {
6723 if (g_locals)
6724 lidx = local_add(ivar);
6725 else
6726 gi = sym_index(ivar);
6727 }
6728 if (lidx >= 0) {
6730 } else {
6732 }
6733
6734 /* comma */
6735 skip_spaces(src, len, pos);
6736 if (*pos >= len || src[*pos] != ',') {
6737 parser_fail(*pos, "Expected ',' between range start and end");
6738 free(ivar);
6739 return;
6740 }
6741 (*pos)++; /* consume ',' */
6742 skip_spaces(src, len, pos);
6743
6744 /* Parse end expression and store in a temp (local or global) */
6745 if (!emit_expression(bc, src, len, pos)) {
6746 parser_fail(*pos, "Expected end expression in range");
6747 free(ivar);
6748 return;
6749 }
6750
6751 char tmpname[64];
6752 snprintf(tmpname, sizeof(tmpname), "__for_end_%d", g_temp_counter++);
6753
6754 int lend = -1, gend = -1;
6755 if (g_locals) {
6756 lend = local_add(tmpname);
6758 } else {
6759 gend = sym_index(tmpname);
6761 }
6762
6763 if (!consume_char(src, len, pos, ')')) {
6764 parser_fail(*pos, "Expected ')' after range arguments");
6765 free(ivar);
6766 return;
6767 }
6768
6769 /* end of header line */
6770 skip_to_eol(src, len, pos);
6771
6772 /* emit loop */
6773 int loop_start = bc->instr_count;
6774
6775 /* condition: ivar < end_tmp */
6776 if (lidx >= 0) {
6778 } else {
6780 }
6781 if (lend >= 0) {
6783 } else {
6785 }
6787 int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
6788
6789 /* enter loop context for break/continue */
6790 LoopCtx ctx = {{0}, 0, {0}, 0, g_loop_ctx};
6791 g_loop_ctx = &ctx;
6792
6793 /* parse body at increased indent (peek) */
6794 int body_indent = 0;
6795 size_t look_body = *pos;
6796 if (read_line_start(src, len, &look_body, &body_indent) && body_indent > current_indent) {
6797 parse_block(bc, src, len, pos, body_indent);
6798 } else {
6799 /* empty body ok */
6800 }
6801
6802 /* continue target: start of increment */
6803 int cont_label = bc->instr_count;
6804
6805 /* i = i + 1 */
6806 int c1 = bytecode_add_constant(bc, make_int(1));
6807 if (lidx >= 0) {
6812 } else {
6817 }
6818
6819 /* back edge and patch */
6820 bytecode_add_instruction(bc, OP_JUMP, loop_start);
6821
6822 /* end label (after loop) */
6823 int end_label = bc->instr_count;
6824 bytecode_set_operand(bc, jmp_false, end_label);
6825
6826 /* patch continue/break jumps */
6827 for (int bi = 0; bi < ctx.cont_count; ++bi) {
6828 bytecode_set_operand(bc, ctx.continue_jumps[bi], cont_label);
6829 }
6830 for (int bi = 0; bi < ctx.break_count; ++bi) {
6831 bytecode_set_operand(bc, ctx.break_jumps[bi], end_label);
6832 }
6833 g_loop_ctx = ctx.prev;
6834
6835 free(ivar);
6836 continue;
6837 } else if (!tuple_mode) {
6838 /* ===== array iteration: for ivar in <expr> ===== */
6839 /* Evaluate the iterable once and store in a temp */
6840 if (!emit_expression(bc, src, len, pos)) {
6841 parser_fail(*pos, "Expected iterable expression after 'in'");
6842 free(ivar);
6843 return;
6844 }
6845 char arrname[64];
6846 snprintf(arrname, sizeof(arrname), "__for_arr_%d", g_temp_counter++);
6847 int larr = -1, garr = -1;
6848 if (g_locals) {
6849 larr = local_add(arrname);
6851 } else {
6852 garr = sym_index(arrname);
6854 }
6855
6856 /* Compute length once: len(arr) -> store temp */
6857 if (larr >= 0) {
6859 } else {
6861 }
6863 char lenname[64];
6864 snprintf(lenname, sizeof(lenname), "__for_len_%d", g_temp_counter++);
6865 int llen = -1, glen = -1;
6866 if (g_locals) {
6867 llen = local_add(lenname);
6869 } else {
6870 glen = sym_index(lenname);
6872 }
6873
6874 /* Index temp: i = 0 */
6875 int c0 = bytecode_add_constant(bc, make_int(0));
6877 char iname[64];
6878 snprintf(iname, sizeof(iname), "__for_i_%d", g_temp_counter++);
6879 int li = -1, gi = -1;
6880 if (g_locals) {
6881 li = local_add(iname);
6883 } else {
6884 gi = sym_index(iname);
6886 }
6887
6888 /* end of header line */
6889 skip_to_eol(src, len, pos);
6890
6891 /* loop start label */
6892 int loop_start = bc->instr_count;
6893
6894 /* condition: i < len */
6895 if (li >= 0) {
6897 } else {
6899 }
6900 if (llen >= 0) {
6902 } else {
6904 }
6906 int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
6907
6908 /* element: ivar = arr[i] */
6909 if (larr >= 0) {
6911 } else {
6913 }
6914 if (li >= 0) {
6916 } else {
6918 }
6920
6921 /* assign to ivar (local preferred) */
6922 int ldst = local_find(ivar);
6923 int gdst = -1;
6924 if (ldst < 0) {
6925 if (g_locals)
6926 ldst = local_add(ivar);
6927 else
6928 gdst = sym_index(ivar);
6929 }
6930 if (ldst >= 0) {
6932 } else {
6934 }
6935
6936 /* enter loop context for break/continue */
6937 LoopCtx ctx = {{0}, 0, {0}, 0, g_loop_ctx};
6938 g_loop_ctx = &ctx;
6939
6940 /* parse body at increased indent */
6941 int body_indent = 0;
6942 size_t look_body = *pos;
6943 if (read_line_start(src, len, &look_body, &body_indent) && body_indent > current_indent) {
6944 parse_block(bc, src, len, pos, body_indent);
6945 } else {
6946 /* empty body ok */
6947 }
6948
6949 /* continue target: start of increment */
6950 int cont_label = bc->instr_count;
6951
6952 /* i = i + 1 */
6953 int c1 = bytecode_add_constant(bc, make_int(1));
6954 if (li >= 0) {
6959 } else {
6964 }
6965
6966 /* back edge and patch */
6967 bytecode_add_instruction(bc, OP_JUMP, loop_start);
6968
6969 /* end label (after loop) */
6970 int end_label = bc->instr_count;
6971 bytecode_set_operand(bc, jmp_false, end_label);
6972
6973 /* patch continue/break jumps */
6974 for (int bi = 0; bi < ctx.cont_count; ++bi) {
6975 bytecode_set_operand(bc, ctx.continue_jumps[bi], cont_label);
6976 }
6977 for (int bi = 0; bi < ctx.break_count; ++bi) {
6978 bytecode_set_operand(bc, ctx.break_jumps[bi], end_label);
6979 }
6980 g_loop_ctx = ctx.prev;
6981
6982 free(ivar);
6983 continue;
6984 } else {
6985 /* ===== map iteration with tuple: for (k, v) in <expr> ===== */
6986 /* Evaluate the map expr once: store in temp */
6987 if (!emit_expression(bc, src, len, pos)) {
6988 parser_fail(*pos, "Expected map expression after 'in'");
6989 free(ivar);
6990 free(vvar);
6991 return;
6992 }
6993 char mapname[64];
6994 snprintf(mapname, sizeof(mapname), "__for_map_%d", g_temp_counter++);
6995 int lmap = -1, gmap = -1;
6996 if (g_locals) {
6997 lmap = local_add(mapname);
6999 } else {
7000 gmap = sym_index(mapname);
7002 }
7003
7004 /* keys = keys(map) */
7005 if (lmap >= 0) {
7007 } else {
7009 }
7011 char keysname[64];
7012 snprintf(keysname, sizeof(keysname), "__for_keys_%d", g_temp_counter++);
7013 int lkeys = -1, gkeys = -1;
7014 if (g_locals) {
7015 lkeys = local_add(keysname);
7017 } else {
7018 gkeys = sym_index(keysname);
7020 }
7021
7022 /* len(keys) */
7023 if (lkeys >= 0) {
7025 } else {
7027 }
7029 char lenname[64];
7030 snprintf(lenname, sizeof(lenname), "__for_klen_%d", g_temp_counter++);
7031 int llen = -1, glen = -1;
7032 if (g_locals) {
7033 llen = local_add(lenname);
7035 } else {
7036 glen = sym_index(lenname);
7038 }
7039
7040 /* i = 0 */
7041 int c0m = bytecode_add_constant(bc, make_int(0));
7043 char iname[64];
7044 snprintf(iname, sizeof(iname), "__for_ki_%d", g_temp_counter++);
7045 int li = -1, gi = -1;
7046 if (g_locals) {
7047 li = local_add(iname);
7049 } else {
7050 gi = sym_index(iname);
7052 }
7053
7054 /* end of header line */
7055 skip_to_eol(src, len, pos);
7056
7057 /* loop start */
7058 int loop_start = bc->instr_count;
7059 /* condition: i < len */
7060 if (li >= 0) {
7062 } else {
7064 }
7065 if (llen >= 0) {
7067 } else {
7069 }
7071 int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
7072
7073 /* key = keys[i] */
7074 if (lkeys >= 0) {
7076 } else {
7078 }
7079 if (li >= 0) {
7081 } else {
7083 }
7085
7086 /* assign to key variable (ivar) */
7087 int lk = local_find(ivar);
7088 int gk = -1;
7089 if (lk < 0) {
7090 if (g_locals)
7091 lk = local_add(ivar);
7092 else
7093 gk = sym_index(ivar);
7094 }
7095 if (lk >= 0) {
7097 } else {
7099 }
7100
7101 /* value = map[key] */
7102 if (lmap >= 0) {
7104 } else {
7106 }
7107 /* load key again */
7108 if (lk >= 0) {
7110 } else {
7112 }
7114
7115 /* assign to value variable (vvar) */
7116 int lv = local_find(vvar);
7117 int gv = -1;
7118 if (lv < 0) {
7119 if (g_locals)
7120 lv = local_add(vvar);
7121 else
7122 gv = sym_index(vvar);
7123 }
7124 if (lv >= 0) {
7126 } else {
7128 }
7129
7130 /* enter loop context */
7131 LoopCtx ctx = {{0}, 0, {0}, 0, g_loop_ctx};
7132 g_loop_ctx = &ctx;
7133
7134 /* body */
7135 int body_indent = 0;
7136 size_t look_body = *pos;
7137 if (read_line_start(src, len, &look_body, &body_indent) && body_indent > current_indent) {
7138 parse_block(bc, src, len, pos, body_indent);
7139 } else {
7140 /* empty body ok */
7141 }
7142
7143 /* continue target: i = i + 1 */
7144 int cont_label = bc->instr_count;
7145 int c1m = bytecode_add_constant(bc, make_int(1));
7146 if (li >= 0) {
7151 } else {
7156 }
7157
7158 /* back edge */
7159 bytecode_add_instruction(bc, OP_JUMP, loop_start);
7160
7161 /* end label */
7162 int end_label = bc->instr_count;
7163 bytecode_set_operand(bc, jmp_false, end_label);
7164
7165 /* patch continue/break */
7166 for (int bi = 0; bi < ctx.cont_count; ++bi) {
7167 bytecode_set_operand(bc, ctx.continue_jumps[bi], cont_label);
7168 }
7169 for (int bi = 0; bi < ctx.break_count; ++bi) {
7170 bytecode_set_operand(bc, ctx.break_jumps[bi], end_label);
7171 }
7172 g_loop_ctx = ctx.prev;
7173
7174 free(ivar);
7175 free(vvar);
7176 continue;
7177 }
7178 }
7179
7180 if (starts_with(src, len, *pos, "if")) {
7181 int end_jumps[64];
7182 int end_count = 0;
7183
7184 for (;;) {
7185 /* consume 'if' or 'else if' condition */
7186 if (starts_with(src, len, *pos, "if")) {
7187 *pos += 2;
7188 } else {
7189 /* for 'else if' we arrive here with *pos already after 'if' */
7190 }
7191
7192 /* require at least one space before condition if present */
7193 skip_spaces(src, len, pos);
7194 if (!emit_expression(bc, src, len, pos)) {
7195 /* no condition -> treat as false */
7196 int ci = bytecode_add_constant(bc, make_int(0));
7198 }
7199
7200 /* Decide between inline single-statement and indented block on next line */
7201 size_t ppeek = *pos;
7202 /* skip spaces after condition */
7203 while (ppeek < len && src[ppeek] == ' ')
7204 ppeek++;
7205 int inline_stmt = 0;
7206 if (ppeek < len) {
7207 if (src[ppeek] == '\r' || src[ppeek] == '\n') {
7208 inline_stmt = 0; /* EOL -> no inline body */
7209 } else if (ppeek + 1 < len && src[ppeek] == '/' && src[ppeek + 1] == '/') {
7210 inline_stmt = 0; /* line comment -> no inline body */
7211 } else if (ppeek + 1 < len && src[ppeek] == '/' && src[ppeek + 1] == '*') {
7212 inline_stmt = 0; /* block comment at EOL -> no inline body */
7213 } else {
7214 inline_stmt = 1; /* there's code after condition on same line */
7215 }
7216 }
7217
7218 /* conditional jump over this clause's inline/body */
7219 int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
7220
7221 if (inline_stmt) {
7222 /* Compile a single inline statement on the same line:
7223 if (cond) <statement> */
7224 *pos = ppeek;
7225 parse_simple_statement(bc, src, len, pos);
7226 /* Skip the inline body when condition is false */
7227 bytecode_set_operand(bc, jmp_false, bc->instr_count);
7228 /* one-liner form has no else/elseif on the same line; end the chain */
7229 break;
7230 }
7231
7232 /* No inline statement on this line: consume up to EOL and parse indented block or else-if/else */
7233 skip_to_eol(src, len, pos);
7234
7235 /* parse nested block if next line is indented */
7236 int next_indent = 0;
7237 size_t look_next = *pos;
7238 if (read_line_start(src, len, &look_next, &next_indent)) {
7239 if (next_indent > current_indent) {
7240 /* parse body at increased indent (let parse_block consume the line) */
7241 parse_block(bc, src, len, pos, next_indent);
7242 } else {
7243 /* empty body; keep *pos at start of that line */
7244 }
7245 } else {
7246 /* EOF -> empty body */
7247 }
7248
7249 /* after body, unconditionally jump to end of the whole chain */
7250 int jmp_end = bytecode_add_instruction(bc, OP_JUMP, 0);
7251 if (end_count < (int)(sizeof(end_jumps) / sizeof(end_jumps[0]))) {
7252 end_jumps[end_count++] = jmp_end;
7253 } else {
7254 parser_fail(*pos, "Too many chained else/if clauses");
7255 return;
7256 }
7257
7258 /* patch false-jump target to start of next clause (or fallthrough) */
7259 bytecode_set_operand(bc, jmp_false, bc->instr_count);
7260
7261 /* look for else or else if at the same indentation */
7262 size_t look = *pos;
7263 int look_indent = 0;
7264 if (!read_line_start(src, len, &look, &look_indent)) {
7265 /* EOF: break and patch end jumps */
7266 break;
7267 }
7268 if (look_indent != current_indent) {
7269 /* dedent or deeper indent means no 'else' clause here */
7270 break;
7271 }
7272 if (starts_with(src, len, look, "else")) {
7273 /* consume 'else' */
7274 *pos = look + 4;
7275 skip_spaces(src, len, pos);
7276
7277 if (starts_with(src, len, *pos, "if")) {
7278 /* else if -> consume 'if' token and continue loop to parse condition */
7279 *pos += 2;
7280 continue;
7281 } else {
7282 /* plain else: parse its block and finish the chain */
7283 skip_to_eol(src, len, pos);
7284 int else_indent = 0;
7285 size_t look_else = *pos;
7286 if (read_line_start(src, len, &look_else, &else_indent) && else_indent > current_indent) {
7287 /* let parse_block consume the line */
7288 parse_block(bc, src, len, pos, else_indent);
7289 } else {
7290 /* empty else-body */
7291 }
7292 /* end of chain after else */
7293 break;
7294 }
7295 } else {
7296 /* next line is not an else/else if */
7297 break;
7298 }
7299 }
7300
7301 /* patch all end-of-clause jumps to the end of the chain */
7302 for (int i = 0; i < end_count; ++i) {
7303 bytecode_set_operand(bc, end_jumps[i], bc->instr_count);
7304 }
7305 continue;
7306 }
7307
7308 /* while loop */
7309 if (starts_with(src, len, *pos, "while")) {
7310 *pos += 5;
7311 skip_spaces(src, len, pos);
7312
7313 int loop_start = bc->instr_count;
7314
7315 /* condition */
7316 if (!emit_expression(bc, src, len, pos)) {
7317 int ci = bytecode_add_constant(bc, make_int(0));
7319 }
7320 /* end of condition */
7321 skip_to_eol(src, len, pos);
7322
7323 /* jump over body if false */
7324 int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
7325
7326 /* enter loop context (continue -> condition) */
7327 LoopCtx ctx = {{0}, 0, {0}, 0, g_loop_ctx};
7328 g_loop_ctx = &ctx;
7329
7330 /* parse body at increased indent (peek indent without advancing pos) */
7331 int body_indent = 0;
7332 size_t look_body = *pos;
7333 if (read_line_start(src, len, &look_body, &body_indent) && body_indent > current_indent) {
7334 parse_block(bc, src, len, pos, body_indent);
7335 } else {
7336 /* empty body allowed */
7337 }
7338
7339 /* patch pending continues to loop_start (re-evaluate condition) */
7340 for (int bi = 0; bi < ctx.cont_count; ++bi) {
7341 bytecode_set_operand(bc, ctx.continue_jumps[bi], loop_start);
7342 }
7343
7344 /* back edge to loop start */
7345 bytecode_add_instruction(bc, OP_JUMP, loop_start);
7346
7347 /* end label and patches */
7348 int end_label = bc->instr_count;
7349 bytecode_set_operand(bc, jmp_false, end_label);
7350 for (int bi = 0; bi < ctx.break_count; ++bi) {
7351 bytecode_set_operand(bc, ctx.break_jumps[bi], end_label);
7352 }
7353 g_loop_ctx = ctx.prev;
7354 continue;
7355 }
7356
7357 /* try/catch/finally */
7358 if (starts_with(src, len, *pos, "try")) {
7359 /* consume 'try' */
7360 *pos += 3;
7361 /* end of header line */
7362 skip_to_eol(src, len, pos);
7363
7364 /* Install a handler placeholder; will be patched to catch label (or a rethrow stub) */
7365 int try_push_idx = bytecode_add_instruction(bc, OP_TRY_PUSH, 0);
7366
7367 /* parse try body at increased indent (if any) */
7368 int try_body_indent = 0;
7369 size_t look_try = *pos;
7370 if (read_line_start(src, len, &look_try, &try_body_indent) && try_body_indent > current_indent) {
7371 parse_block(bc, src, len, pos, try_body_indent);
7372 } else {
7373 /* empty try body allowed */
7374 }
7375
7376 /* After try body, pop handler for normal (non-exceptional) flow */
7378
7379 /* on normal completion, jump over catch body */
7380 int jmp_over_catch_finally = bytecode_add_instruction(bc, OP_JUMP, 0);
7381
7382 /* Optional: catch and/or finally clauses at same indentation */
7383 int seen_catch = 0;
7384 int seen_finally = 0;
7385 int catch_label = -1;
7386 for (;;) {
7387 size_t look = *pos;
7388 int look_indent = 0;
7389 if (!read_line_start(src, len, &look, &look_indent)) break; /* EOF */
7390 if (look_indent != current_indent) break; /* different indentation -> stop */
7391
7392 if (!seen_catch && starts_with(src, len, look, "catch")) {
7393 /* consume 'catch' */
7394 *pos = look + 5;
7395 /* optional variable name */
7396 skip_spaces(src, len, pos);
7397 char *ex_name = NULL;
7398 size_t tmp = *pos;
7399 int have_name = 0;
7400 if (read_identifier_into(src, len, &tmp, &ex_name)) {
7401 *pos = tmp;
7402 have_name = 1;
7403 }
7404 /* end of header line */
7405 skip_to_eol(src, len, pos);
7406
7407 /* Mark catch label and patch try handler target */
7408 catch_label = bc->instr_count;
7409 bytecode_set_operand(bc, try_push_idx, catch_label);
7410
7411 /* On entering catch, the thrown error is on stack. Bind to name if provided, else pop. */
7412 if (have_name) {
7413 int lidx = -1, gi = -1;
7414 if (g_locals) {
7415 int existing = local_find(ex_name);
7416 if (existing >= 0)
7417 lidx = existing;
7418 else
7419 lidx = local_add(ex_name);
7420 } else {
7421 gi = sym_index(ex_name);
7422 }
7423 if (lidx >= 0)
7425 else if (gi >= 0)
7427 else
7429 } else {
7431 }
7432 if (ex_name) free(ex_name);
7433
7434 /* parse catch body at increased indent (if any) */
7435 int catch_indent = 0;
7436 size_t look_catch = *pos;
7437 if (read_line_start(src, len, &look_catch, &catch_indent) && catch_indent > current_indent) {
7438 parse_block(bc, src, len, pos, catch_indent);
7439 } else {
7440 /* empty catch body allowed */
7441 }
7442 seen_catch = 1;
7443 continue;
7444 }
7445
7446 if (!seen_finally && starts_with(src, len, look, "finally")) {
7447 /* consume 'finally' */
7448 *pos = look + 7;
7449 /* end of header line */
7450 skip_to_eol(src, len, pos);
7451
7452 /* parse finally body at increased indent (if any) */
7453 int finally_indent = 0;
7454 size_t look_fin = *pos;
7455 if (read_line_start(src, len, &look_fin, &finally_indent) && finally_indent > current_indent) {
7456 parse_block(bc, src, len, pos, finally_indent);
7457 } else {
7458 /* empty finally body allowed */
7459 }
7460
7461 seen_finally = 1;
7462 continue;
7463 }
7464
7465 /* no recognized clause at this indentation */
7466 break;
7467 }
7468
7469 /* If no catch clause was present, make handler rethrow */
7470 if (!seen_catch) {
7471 int rethrow_label = bc->instr_count;
7472 bytecode_set_operand(bc, try_push_idx, rethrow_label);
7473 /* at handler: immediately rethrow the incoming error */
7475 }
7476
7477 /* patch normal-flow jump to here (after catch/finally) */
7478 bytecode_set_operand(bc, jmp_over_catch_finally, bc->instr_count);
7479 continue;
7480 }
7481
7482 /* otherwise: simple statement on this line */
7483 parse_simple_statement(bc, src, len, pos);
7484 }
7485}
7486
7498static Bytecode *compile_minimal(const char *src, size_t len) {
7499 Bytecode *bc = bytecode_new();
7500 size_t pos = 0;
7501
7502 /* Refresh namespace alias table for this compilation unit */
7503 ns_aliases_reset();
7504 ns_aliases_scan(src, len);
7505
7506 skip_shebang_if_present(src, len, &pos);
7507
7508 /* Allow top-of-file function definitions; do not skip any leading 'fun' line */
7509 skip_comments(src, len, &pos);
7510 skip_ws(src, len, &pos);
7511
7512 /* parse the top-level block at indent 0 */
7513 parse_block(bc, src, len, &pos, 0);
7514
7516 return bc;
7517}
7518
7530 size_t len = 0;
7531 char *src = read_file_all(path, &len);
7532 if (!src) {
7533 fprintf(stderr, "Error: cannot read file: %s\n", path);
7534 return NULL;
7535 }
7536
7537 /* Preprocess includes before compiling (with path for accurate markers) */
7538 char *prep = preprocess_includes_with_path(src, path);
7539 const char *compile_src = prep ? prep : src;
7540 size_t compile_len = strlen(compile_src);
7541
7542 /* reset error state */
7543 g_has_error = 0;
7544 g_err_pos = 0;
7545 g_err_msg[0] = '\0';
7546 g_err_line = 0;
7547 g_err_col = 0;
7548
7549 /* Set current source path for nested bytecodes to inherit */
7550 const char *prev_source = g_current_source_path;
7551 g_current_source_path = path;
7552 Bytecode *bc = compile_minimal(compile_src, compile_len);
7553 /* assign debug metadata to module bytecode */
7554 if (bc) {
7555 if (bc->source_file) free((void *)bc->source_file);
7556 bc->source_file = path ? strdup(path) : strdup("<input>");
7557 if (bc->name) free((void *)bc->name);
7558 /* derive name from basename of path */
7559 const char *bn = path ? strrchr(path, '/') : NULL;
7560 const char *base = bn ? bn + 1 : (path ? path : "<input>");
7561 bc->name = strdup(base);
7562 }
7563 /* restore previous */
7564 g_current_source_path = prev_source;
7565
7566 if (g_has_error) {
7567 int line = 1, col = 1;
7568 calc_line_col(compile_src, compile_len, g_err_pos, &line, &col);
7569
7570 /* If the preprocessor injected an initial include marker line, compensate
7571 * it in the outward-reported top-level line number so it matches the
7572 * physical file. The marker may appear on the first line OR on the second
7573 * line if a shebang was preserved as line 1. We therefore check the first
7574 * non-shebang line for the marker and, if the error lies after that line,
7575 * subtract exactly one from the reported line. */
7576 {
7577 const char *marker0 = "// __include_begin__: ";
7578 size_t m0 = strlen(marker0);
7579
7580 /* Determine start of first logical line to examine (skip shebang) */
7581 size_t start = 0;
7582 if (compile_len >= 2 && compile_src[0] == '#' && compile_src[1] == '!') {
7583 /* skip to end of shebang line (handle CR/LF/CRLF) */
7584 while (start < compile_len && compile_src[start] != '\n' && compile_src[start] != '\r') start++;
7585 if (start < compile_len && compile_src[start] == '\r') {
7586 start++;
7587 if (start < compile_len && compile_src[start] == '\n') start++;
7588 } else if (start < compile_len && compile_src[start] == '\n') {
7589 start++;
7590 }
7591 }
7592
7593 /* Find beginning and end of that (first non-shebang) line */
7594 size_t ls = start;
7595 size_t eol0 = ls;
7596 while (eol0 < compile_len && compile_src[eol0] != '\n') eol0++;
7597
7598 if (ls + m0 <= compile_len && strncmp(compile_src + ls, marker0, m0) == 0) {
7599 /* If the error is positioned after the synthetic marker line, reduce the outward line */
7600 if (g_err_pos > eol0) {
7601 if (line > 1) line -= 1;
7602 }
7603 }
7604 }
7605
7606 g_err_line = line;
7607 g_err_col = col;
7608
7609 /* Try to locate include context marker preceding error */
7610 const char *marker = "// __include_begin__: ";
7611 size_t mlen = strlen(marker);
7612 int inner_line = -1;
7613 int base_line = 1;
7614 char inc_path[512];
7615 inc_path[0] = '\0';
7616 /* scan backward to find last marker line */
7617 size_t scan = g_err_pos;
7618 while (scan > 0) {
7619 /* find start of current line */
7620 size_t ls = scan;
7621 while (ls > 0 && compile_src[ls - 1] != '\n')
7622 ls--;
7623 /* check if this line starts with marker */
7624 if (ls + mlen <= compile_len && strncmp(compile_src + ls, marker, mlen) == 0) {
7625 /* Parse marker: path [as alias] [@line N] */
7626 size_t p = ls + mlen;
7627 size_t eol = p;
7628 while (eol < compile_len && compile_src[eol] != '\n') eol++;
7629 /* locate separators */
7630 size_t pos_as = eol, pos_line = eol;
7631 for (size_t t = p; t + 3 < eol; ++t) {
7632 if (compile_src[t] == ' ' && strncmp(compile_src + t, " as ", 4) == 0) { pos_as = t; break; }
7633 }
7634 for (size_t t = p; t + 6 < eol; ++t) {
7635 if (compile_src[t] == ' ' && strncmp(compile_src + t, " @line ", 7) == 0) { pos_line = t; break; }
7636 }
7637 size_t path_end = pos_as < pos_line ? pos_as : pos_line;
7638 if (path_end < p) path_end = eol;
7639 size_t copy = (path_end - p) < sizeof(inc_path) - 1 ? (path_end - p) : sizeof(inc_path) - 1;
7640 memcpy(inc_path, compile_src + p, copy);
7641 inc_path[copy] = '\0';
7642
7643 /* parse optional base line value */
7644 base_line = 1;
7645 if (pos_line < eol) {
7646 size_t num_start = pos_line + 7;
7647 while (num_start < eol && compile_src[num_start] == ' ') num_start++;
7648 int v = 0;
7649 while (num_start < eol && compile_src[num_start] >= '0' && compile_src[num_start] <= '9') {
7650 v = v * 10 + (compile_src[num_start] - '0');
7651 num_start++;
7652 }
7653 if (v > 0) base_line = v;
7654 }
7655
7656 /* compute inner line as number of newlines from (eol+1) to error position */
7657 int count = 1;
7658 size_t q = (eol < compile_len && compile_src[eol] == '\n') ? (eol + 1) : eol;
7659 while (q < g_err_pos) {
7660 if (compile_src[q] == '\n') count++;
7661 q++;
7662 }
7663 inner_line = count;
7664 break;
7665 }
7666 /* move to previous line */
7667 if (ls == 0) break;
7668 scan = ls - 1;
7669 }
7670
7671 if (inner_line > 0 && inc_path[0] != '\0') {
7672 /* Adjust for shebang in included physical file */
7673 int shebang_adjust = 0;
7674 FILE *sf = fopen(inc_path, "rb");
7675 if (sf) {
7676 int c1 = fgetc(sf);
7677 int c2 = fgetc(sf);
7678 if (c1 == '#' && c2 == '!') shebang_adjust = 1;
7679 fclose(sf);
7680 }
7681 int mapped_inner = inner_line + (base_line - 1) + shebang_adjust;
7682 /* If the include context points to the same top-level path, suppress the redundant trailer. */
7683 if (path && strcmp(path, inc_path) == 0) {
7684 fprintf(stderr, "Parse error %s:%d:%d: %s\n",
7685 path, line, col, g_err_msg);
7686 } else {
7687 fprintf(stderr, "Parse error %s:%d:%d: %s (in %s:%d)\n",
7688 path ? path : "<input>", line, col, g_err_msg, inc_path, mapped_inner);
7689 }
7690 } else {
7691 fprintf(stderr, "Parse error %s:%d:%d: %s\n", path ? path : "<input>", line, col, g_err_msg);
7692 }
7693
7694 if (bc) bytecode_free(bc);
7695 if (prep) free(prep);
7696 free(src);
7697 return NULL;
7698 }
7699
7700 if (prep) free(prep);
7701 free(src);
7702 return bc;
7703}
7704
7714Bytecode *parse_string_to_bytecode(const char *source) {
7715 if (!source) {
7716 fprintf(stderr, "Error: null source provided\n");
7717 return NULL;
7718 }
7719
7720 /* Preprocess includes before compiling */
7721 char *prep = preprocess_includes(source);
7722 const char *compile_src = prep ? prep : source;
7723 size_t len = strlen(compile_src);
7724
7725 /* reset error state */
7726 g_has_error = 0;
7727 g_err_pos = 0;
7728 g_err_msg[0] = '\0';
7729 g_err_line = 0;
7730 g_err_col = 0;
7731
7732 /* Set current source path to <input> for nested bytecodes */
7733 const char *prev_src = g_current_source_path;
7734 g_current_source_path = NULL;
7735 Bytecode *bc = compile_minimal(compile_src, len);
7736 if (bc) {
7737 if (bc->source_file) free((void *)bc->source_file);
7738 bc->source_file = strdup("<input>");
7739 if (bc->name) free((void *)bc->name);
7740 bc->name = strdup("<input>");
7741 }
7742 g_current_source_path = prev_src;
7743
7744 if (g_has_error) {
7745 int line = 1, col = 1;
7746 calc_line_col(compile_src, len, g_err_pos, &line, &col);
7747 g_err_line = line;
7748 g_err_col = col;
7749 if (bc) bytecode_free(bc);
7750 if (prep) free(prep);
7751 return NULL;
7752 }
7753 if (prep) free(prep);
7754 return bc;
7755}
7756
7770int parser_last_error(char *msgBuf, unsigned long msgCap, int *outLine, int *outCol) {
7771 if (!g_has_error) return 0;
7772 if (msgBuf && msgCap > 0) {
7773 snprintf(msgBuf, msgCap, "%s", g_err_msg);
7774 }
7775 if (outLine) *outLine = g_err_line;
7776 if (outCol) *outCol = g_err_col;
7777 return 1;
7778}
Bytecode * bytecode_new(void)
Allocate and initialize an empty Bytecode object.
Definition bytecode.c:27
int bytecode_add_instruction(Bytecode *bc, OpCode op, int32_t operand)
Append a single instruction to the instruction stream.
Definition bytecode.c:62
void bytecode_free(Bytecode *bc)
Free a Bytecode and all memory it owns.
Definition bytecode.c:92
void bytecode_dump(const Bytecode *bc)
Print a human-readable dump of constants and instructions to stdout.
Definition bytecode.c:423
void bytecode_set_operand(Bytecode *bc, int idx, int32_t operand)
Patch the operand of a previously emitted instruction.
Definition bytecode.c:78
int bytecode_add_constant(Bytecode *bc, Value v)
Append a constant to a Bytecode's constant table.
Definition bytecode.c:48
@ OP_OPENSSL_SHA512
Definition bytecode.h:199
@ OP_PCSC_RELEASE
Definition bytecode.h:185
@ OP_GCD
Definition bytecode.h:268
@ OP_JSON_PARSE
Definition bytecode.h:167
@ OP_READ_FILE
Definition bytecode.h:136
@ OP_CLOCK_MONO_MS
Definition bytecode.h:145
@ OP_CALL
Definition bytecode.h:60
@ OP_RUST_HELLO_ARGS_RETURN
Definition bytecode.h:280
@ OP_ROTL
Definition bytecode.h:163
@ OP_TAN
Definition bytecode.h:261
@ OP_OPENSSL_SHA256
Definition bytecode.h:198
@ OP_JSON_STRINGIFY
Definition bytecode.h:168
@ OP_PCSC_TRANSMIT
Definition bytecode.h:189
@ OP_TRUNC
Definition bytecode.h:255
@ OP_CEIL
Definition bytecode.h:254
@ OP_SPLIT
Definition bytecode.h:101
@ OP_CAST
Definition bytecode.h:95
@ OP_EQ
Definition bytecode.h:53
@ OP_PCRE2_MATCH
Definition bytecode.h:193
@ OP_RANDOM_INT
Definition bytecode.h:127
@ OP_TRY_POP
Definition bytecode.h:249
@ OP_LTE
Definition bytecode.h:50
@ OP_SOCK_TCP_ACCEPT
Definition bytecode.h:221
@ OP_NEQ
Definition bytecode.h:54
@ OP_ISQRT
Definition bytecode.h:270
@ OP_FMAX
Definition bytecode.h:275
@ OP_CPP_ADD
Definition bytecode.h:285
@ OP_PCSC_ESTABLISH
Definition bytecode.h:184
@ OP_SQLITE_EXEC
Definition bytecode.h:180
@ OP_FMIN
Definition bytecode.h:274
@ OP_SWAP
Definition bytecode.h:76
@ OP_SOCK_TCP_LISTEN
Definition bytecode.h:220
@ OP_OPENSSL_RIPEMD160
Definition bytecode.h:200
@ OP_PCSC_LIST_READERS
Definition bytecode.h:186
@ OP_BOR
Definition bytecode.h:158
@ OP_CURL_POST
Definition bytecode.h:174
@ OP_XML_NAME
Definition bytecode.h:216
@ OP_REGEX_SEARCH
Definition bytecode.h:108
@ OP_POW
Definition bytecode.h:125
@ OP_NOT
Definition bytecode.h:73
@ OP_SQRT
Definition bytecode.h:265
@ OP_SOCK_RECV
Definition bytecode.h:224
@ OP_MOD
Definition bytecode.h:70
@ OP_INI_UNSET
Definition bytecode.h:210
@ OP_WRITE_FILE
Definition bytecode.h:137
@ OP_SQLITE_CLOSE
Definition bytecode.h:179
@ OP_FIND
Definition bytecode.h:104
@ OP_SIGN
Definition bytecode.h:271
@ OP_DUP
Definition bytecode.h:75
@ OP_OS_LIST_DIR
Definition bytecode.h:238
@ OP_ENV_ALL
Definition bytecode.h:147
@ OP_VALUES
Definition bytecode.h:132
@ OP_RANDOM_NUMBER
Definition bytecode.h:154
@ OP_INDEX_GET
Definition bytecode.h:80
@ OP_LCM
Definition bytecode.h:269
@ OP_LEN
Definition bytecode.h:84
@ OP_THREAD_SPAWN
Definition bytecode.h:151
@ OP_INDEX_OF
Definition bytecode.h:113
@ OP_LINE
Definition bytecode.h:67
@ OP_MIN
Definition bytecode.h:121
@ OP_SLEEP_MS
Definition bytecode.h:153
@ OP_REGEX_REPLACE
Definition bytecode.h:109
@ OP_MAX
Definition bytecode.h:122
@ OP_SQLITE_OPEN
Definition bytecode.h:178
@ OP_XML_PARSE
Definition bytecode.h:214
@ OP_STORE_LOCAL
Definition bytecode.h:39
@ OP_SLICE
Definition bytecode.h:90
@ OP_INI_GET_DOUBLE
Definition bytecode.h:207
@ OP_JOIN
Definition bytecode.h:102
@ OP_COS
Definition bytecode.h:260
@ OP_BAND
Definition bytecode.h:157
@ OP_ECHO
Definition bytecode.h:64
@ OP_ROTR
Definition bytecode.h:164
@ OP_DATE_FORMAT
Definition bytecode.h:146
@ OP_PROC_SYSTEM
Definition bytecode.h:143
@ OP_OPENSSL_MD5
Definition bytecode.h:197
@ OP_ZIP
Definition bytecode.h:118
@ OP_SUB
Definition bytecode.h:45
@ OP_INI_GET_INT
Definition bytecode.h:206
@ OP_XML_ROOT
Definition bytecode.h:215
@ OP_JUMP
Definition bytecode.h:57
@ OP_DIV
Definition bytecode.h:47
@ OP_SHR
Definition bytecode.h:162
@ OP_SERIAL_OPEN
Definition bytecode.h:241
@ OP_JSON_TO_FILE
Definition bytecode.h:170
@ OP_LT
Definition bytecode.h:49
@ OP_INI_GET_BOOL
Definition bytecode.h:208
@ OP_ROUND
Definition bytecode.h:256
@ OP_PRINT
Definition bytecode.h:63
@ OP_INI_GET_STRING
Definition bytecode.h:205
@ OP_SHL
Definition bytecode.h:161
@ OP_KEYS
Definition bytecode.h:131
@ OP_RUST_HELLO
Definition bytecode.h:278
@ OP_LOAD_LOCAL
Definition bytecode.h:38
@ OP_CURL_DOWNLOAD
Definition bytecode.h:175
@ OP_PCSC_CONNECT
Definition bytecode.h:187
@ OP_RUST_SET_EXIT
Definition bytecode.h:282
@ OP_ENV
Definition bytecode.h:140
@ OP_PUSH
Definition bytecode.h:85
@ OP_TO_NUMBER
Definition bytecode.h:93
@ OP_LOAD_GLOBAL
Definition bytecode.h:41
@ OP_FUN_VERSION
Definition bytecode.h:148
@ OP_SERIAL_SEND
Definition bytecode.h:243
@ OP_LOAD_CONST
Definition bytecode.h:37
@ OP_ADD
Definition bytecode.h:44
@ OP_CONTAINS
Definition bytecode.h:112
@ OP_SOCK_SEND
Definition bytecode.h:223
@ OP_INPUT_LINE
Definition bytecode.h:141
@ OP_FD_POLL_READ
Definition bytecode.h:231
@ OP_MUL
Definition bytecode.h:46
@ OP_SERIAL_CONFIG
Definition bytecode.h:242
@ OP_SOCK_UNIX_CONNECT
Definition bytecode.h:227
@ OP_PCRE2_FINDALL
Definition bytecode.h:194
@ OP_SET
Definition bytecode.h:87
@ OP_PROC_RUN
Definition bytecode.h:142
@ OP_LOG10
Definition bytecode.h:264
@ OP_ENUMERATE
Definition bytecode.h:117
@ OP_FD_POLL_WRITE
Definition bytecode.h:232
@ OP_PCRE2_TEST
Definition bytecode.h:192
@ OP_SIN
Definition bytecode.h:259
@ OP_TIME_NOW_MS
Definition bytecode.h:144
@ OP_SCLAMP
Definition bytecode.h:98
@ OP_XML_TEXT
Definition bytecode.h:217
@ OP_FD_SET_NONBLOCK
Definition bytecode.h:230
@ OP_STORE_GLOBAL
Definition bytecode.h:42
@ OP_SOCK_TCP_CONNECT
Definition bytecode.h:222
@ OP_SUBSTR
Definition bytecode.h:103
@ OP_UCLAMP
Definition bytecode.h:97
@ OP_INI_SET
Definition bytecode.h:209
@ OP_TO_STRING
Definition bytecode.h:94
@ OP_SOCK_CLOSE
Definition bytecode.h:225
@ OP_GT
Definition bytecode.h:51
@ OP_INI_SAVE
Definition bytecode.h:211
@ OP_SERIAL_RECV
Definition bytecode.h:244
@ OP_GTE
Definition bytecode.h:52
@ OP_HALT
Definition bytecode.h:65
@ OP_ABS
Definition bytecode.h:124
@ OP_INI_LOAD
Definition bytecode.h:203
@ OP_TYPEOF
Definition bytecode.h:96
@ OP_RETURN
Definition bytecode.h:61
@ OP_REGEX_MATCH
Definition bytecode.h:107
@ OP_EXIT
Definition bytecode.h:235
@ OP_SQLITE_QUERY
Definition bytecode.h:181
@ OP_APOP
Definition bytecode.h:86
@ OP_SOCK_UNIX_LISTEN
Definition bytecode.h:226
@ OP_BNOT
Definition bytecode.h:160
@ OP_THREAD_JOIN
Definition bytecode.h:152
@ OP_CLAMP
Definition bytecode.h:123
@ OP_SERIAL_CLOSE
Definition bytecode.h:245
@ OP_RUST_HELLO_ARGS
Definition bytecode.h:279
@ OP_CURL_GET
Definition bytecode.h:173
@ OP_MAKE_ARRAY
Definition bytecode.h:79
@ OP_TRY_PUSH
Definition bytecode.h:248
@ OP_CLEAR
Definition bytecode.h:114
@ OP_RANDOM_SEED
Definition bytecode.h:126
@ OP_INDEX_SET
Definition bytecode.h:81
@ OP_LOG
Definition bytecode.h:263
@ OP_INI_FREE
Definition bytecode.h:204
@ OP_REMOVE
Definition bytecode.h:89
@ OP_THROW
Definition bytecode.h:250
@ OP_HAS_KEY
Definition bytecode.h:133
@ OP_PCSC_DISCONNECT
Definition bytecode.h:188
@ OP_FLOOR
Definition bytecode.h:253
@ OP_JSON_FROM_FILE
Definition bytecode.h:169
@ OP_INSERT
Definition bytecode.h:88
@ OP_EXP
Definition bytecode.h:262
@ OP_RUST_GET_SP
Definition bytecode.h:281
@ OP_JUMP_IF_FALSE
Definition bytecode.h:58
@ OP_MAKE_MAP
Definition bytecode.h:130
@ OP_POP
Definition bytecode.h:56
@ OP_BXOR
Definition bytecode.h:159
Value v
Definition cast.c:22
int k
Definition cast.c:29
int ok
Definition contains.c:38
const char * name
Definition env.c:29
int idx
Definition index_of.c:38
size_t len
Definition input_line.c:102
int n
Definition insert.c:41
free(vals)
int is_class[MAX_GLOBALS]
Definition parser.c:265
#define TYPE_META_NIL
Type metadata tag indicating explicit nil type.
Definition parser.c:124
char * names[MAX_GLOBALS]
Definition parser.c:263
int types[MAX_GLOBALS]
Definition parser.c:264
#define TYPE_META_BOOLEAN
Type metadata tag used for boolean enforcement in declared types.
Definition parser.c:122
#define TYPE_META_ARRAY
Type metadata tag marking array values.
Definition parser.c:130
#define TYPE_META_FLOAT
Type metadata tag marking floating point numbers.
Definition parser.c:128
Bytecode * parse_string_to_bytecode(const char *source)
Parse a source string and return compiled bytecode.
Definition parser.c:7714
int parser_last_error(char *msgBuf, unsigned long msgCap, int *outLine, int *outCol)
Retrieve the last parser/compiler error information, if any.
Definition parser.c:7770
#define MAP_TYPE_KIND(t)
#define TYPE_META_CLASS
Type metadata tag marking class/instance values.
Definition parser.c:126
#define TYPE_META_STRING
Type metadata tag used for string enforcement in declared types.
Definition parser.c:120
int count
Definition parser.c:266
char * preprocess_includes_with_path(const char *src, const char *current_path)
Preprocess includes with a known file path to improve span markers.
Bytecode * parse_file_to_bytecode(const char *path)
Parse a .fun source file and return compiled bytecode.
Definition parser.c:7529
Public API for parsing Fun source into bytecode.
Low-level parsing helpers and include preprocessor for the Fun parser.
char * preprocess_includes(const char *src)
Public wrapper to preprocess includes without a current path.
int64_t exp
Definition pow.c:39
int64_t base
Definition pow.c:38
fclose(f)
const char * p
Definition read_file.c:37
uint32_t s
Definition rol.c:31
long t
Definition sleep_ms.c:32
Value start
Definition slice.c:34
const char * source_file
Definition bytecode.h:302
const char * name
Definition bytecode.h:301
int instr_count
Definition bytecode.h:295
int types[MAX_FRAME_LOCALS]
Definition parser.c:308
char * names[MAX_FRAME_LOCALS]
Definition parser.c:307
int count
Definition parser.c:309
struct LoopCtx * prev
Definition parser.c:344
int break_count
Definition parser.c:341
int cont_count
Definition parser.c:343
int continue_jumps[64]
Definition parser.c:342
int break_jumps[64]
Definition parser.c:340
const char * tname
Definition typeof.c:26
Value make_bool(int v)
Construct a boolean Value.
Definition value.c:79
Value make_nil(void)
Construct a nil Value.
Definition value.c:126
Value make_string(const char *s)
Construct a string Value by duplicating the given C string.
Definition value.c:95
Value make_function(struct Bytecode *fn)
Construct a function Value referencing bytecode.
Definition value.c:114
Value make_float(double v)
Construct a Value representing a double-precision float.
Definition value.c:64
Value make_int(int64_t v)
Construct a Value representing a 64-bit integer.
Definition value.c:51
Defines the Value type and associated functions for the Fun VM.
#define fprintf
Definition vm.c:200
Core virtual machine data structures and public VM API.
#define MAX_GLOBALS
Definition vm.h:34
#define MAX_FRAME_LOCALS
Definition vm.h:30
Value path
Definition write_file.c:33