nebulara 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/Compiler/nbs-bootstrap.c +2229 -0
  2. package/Compiler/nbs_cli.c +1553 -0
  3. package/Compiler/neb-cli.exe +0 -0
  4. package/Compiler/neb-codegen.c +287 -0
  5. package/Compiler/neb-ffi.c +303 -0
  6. package/Compiler/neb-ir.c +307 -0
  7. package/Compiler/neb-knowledge.c +203 -0
  8. package/Compiler/neb-pipeline.c +787 -0
  9. package/Compiler/neb-semantic.c +261 -0
  10. package/Compiler/nebulara.exe +0 -0
  11. package/README.md +258 -0
  12. package/SPEC.md +267 -0
  13. package/bin/neb.js +36 -0
  14. package/build/neb-cli.exe +0 -0
  15. package/build/neb-codegen.exe +0 -0
  16. package/build/neb-ffi.exe +0 -0
  17. package/build/neb-knowledge.exe +0 -0
  18. package/build/neb-pipeline.exe +0 -0
  19. package/build/nebulara.exe +0 -0
  20. package/dist/index.js +131 -0
  21. package/package.json +39 -0
  22. package/std/collections.nbs +72 -0
  23. package/std/json.nbs +15 -0
  24. package/std/kanban.nbs +122 -0
  25. package/std/math.nbs +52 -0
  26. package/std/net.nbs +22 -0
  27. package/std/primitives.nbs +30 -0
  28. package/std/string.nbs +71 -0
  29. package/std/time.nbs +15 -0
  30. package/test/broken.nbs +2 -0
  31. package/test/hello.nbs +6 -0
  32. package/test/test-arrays.nbs +22 -0
  33. package/test/test-charat.nbs +7 -0
  34. package/test/test-cont-debug.nbs +8 -0
  35. package/test/test-cont-min.nbs +7 -0
  36. package/test/test-cont-parens.nbs +7 -0
  37. package/test/test-cont2.nbs +7 -0
  38. package/test/test-cont3.nbs +7 -0
  39. package/test/test-control.nbs +18 -0
  40. package/test/test-functions.nbs +13 -0
  41. package/test/test-loops.nbs +17 -0
  42. package/test/test-mod-for.nbs +6 -0
  43. package/test/test-mod.nbs +5 -0
  44. package/test/test-new-features.nbs +39 -0
  45. package/test/test-recursion.nbs +23 -0
  46. package/test/test-runtime.nbs +8 -0
  47. package/test/test-strings.nbs +14 -0
  48. package/test/test-trycatch.nbs +11 -0
@@ -0,0 +1,2229 @@
1
+ // Nebulara Self-Hosted Interpreter
2
+ // Compiler/nbs-bootstrap.c - Interprets .nbs programs directly
3
+ // Phase 2: Full interpreter with strings, control flow, functions, arrays
4
+
5
+ #include <stdint.h>
6
+ #include <stdio.h>
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+ #include <ctype.h>
10
+ #include <stdarg.h>
11
+ #include <time.h>
12
+ #include <math.h>
13
+ #include <errno.h>
14
+
15
+ // ============================================================================
16
+ // VALUE SYSTEM — Tagged union for dynamic typing
17
+ // ============================================================================
18
+
19
+ #define VAL_NULL 0
20
+ #define VAL_INT 1
21
+ #define VAL_STRING 2
22
+ #define VAL_BOOL 3
23
+ #define VAL_ARRAY 4
24
+ #define VAL_FUNC 5
25
+
26
+ typedef struct Value Value;
27
+ typedef struct ValueArray ValueArray;
28
+
29
+ struct Value {
30
+ int type;
31
+ union {
32
+ int64_t i;
33
+ char* s;
34
+ int b;
35
+ ValueArray* a;
36
+ int func_idx;
37
+ } as;
38
+ };
39
+
40
+ struct ValueArray {
41
+ Value* items;
42
+ int count;
43
+ int capacity;
44
+ };
45
+
46
+ Value val_null(void) { return (Value){VAL_NULL, {0}}; }
47
+ Value val_int(int64_t v) { return (Value){VAL_INT, {.i = v}}; }
48
+ Value val_bool(int v) { return (Value){VAL_BOOL, {.b = v}}; }
49
+ Value val_string(const char* s) {
50
+ Value v; v.type = VAL_STRING;
51
+ v.as.s = (char*)malloc(strlen(s) + 1);
52
+ strcpy(v.as.s, s);
53
+ return v;
54
+ }
55
+ Value val_array(void) {
56
+ Value v; v.type = VAL_ARRAY;
57
+ v.as.a = (ValueArray*)calloc(1, sizeof(ValueArray));
58
+ return v;
59
+ }
60
+
61
+ void val_free(Value v) {
62
+ if (v.type == VAL_STRING) free(v.as.s);
63
+ if (v.type == VAL_ARRAY && v.as.a) {
64
+ for (int i = 0; i < v.as.a->count; i++) val_free(v.as.a->items[i]);
65
+ free(v.as.a->items);
66
+ free(v.as.a);
67
+ }
68
+ }
69
+
70
+ Value val_copy(Value v) {
71
+ if (v.type == VAL_STRING) return val_string(v.as.s);
72
+ if (v.type == VAL_ARRAY) {
73
+ Value arr = val_array();
74
+ for (int i = 0; i < v.as.a->count; i++) {
75
+ if (arr.as.a->count >= arr.as.a->capacity) {
76
+ arr.as.a->capacity = arr.as.a->capacity ? arr.as.a->capacity * 2 : 8;
77
+ arr.as.a->items = (Value*)realloc(arr.as.a->items, arr.as.a->capacity * sizeof(Value));
78
+ }
79
+ arr.as.a->items[arr.as.a->count++] = val_copy(v.as.a->items[i]);
80
+ }
81
+ return arr;
82
+ }
83
+ return v;
84
+ }
85
+
86
+ int val_is_truthy(Value v) {
87
+ if (v.type == VAL_NULL) return 0;
88
+ if (v.type == VAL_BOOL) return v.as.b;
89
+ if (v.type == VAL_INT) return v.as.i != 0;
90
+ if (v.type == VAL_STRING) return v.as.s[0] != 0;
91
+ if (v.type == VAL_ARRAY) return v.as.a->count > 0;
92
+ return 1;
93
+ }
94
+
95
+ const char* val_type_name(Value v) {
96
+ switch (v.type) {
97
+ case VAL_NULL: return "null";
98
+ case VAL_INT: return "int";
99
+ case VAL_STRING: return "string";
100
+ case VAL_BOOL: return "bool";
101
+ case VAL_ARRAY: return "array";
102
+ case VAL_FUNC: return "func";
103
+ default: return "unknown";
104
+ }
105
+ }
106
+
107
+ Value val_to_string(Value v) {
108
+ char buf[128];
109
+ switch (v.type) {
110
+ case VAL_NULL: return val_string("null");
111
+ case VAL_INT: snprintf(buf, sizeof(buf), "%lld", v.as.i); return val_string(buf);
112
+ case VAL_BOOL: return val_string(v.as.b ? "true" : "false");
113
+ case VAL_STRING: return val_string(v.as.s);
114
+ case VAL_ARRAY: snprintf(buf, sizeof(buf), "[array %d]", v.as.a->count); return val_string(buf);
115
+ default: return val_string("unknown");
116
+ }
117
+ }
118
+
119
+ Value val_add(Value a, Value b) {
120
+ if (a.type == VAL_INT && b.type == VAL_INT) return val_int(a.as.i + b.as.i);
121
+ if (a.type == VAL_ARRAY && b.type == VAL_ARRAY) {
122
+ Value result = val_array();
123
+ for (int i = 0; i < a.as.a->count; i++) {
124
+ if (result.as.a->count >= result.as.a->capacity) {
125
+ result.as.a->capacity = result.as.a->capacity ? result.as.a->capacity * 2 : 8;
126
+ result.as.a->items = (Value*)realloc(result.as.a->items, result.as.a->capacity * sizeof(Value));
127
+ }
128
+ result.as.a->items[result.as.a->count++] = val_copy(a.as.a->items[i]);
129
+ }
130
+ for (int i = 0; i < b.as.a->count; i++) {
131
+ if (result.as.a->count >= result.as.a->capacity) {
132
+ result.as.a->capacity = result.as.a->capacity ? result.as.a->capacity * 2 : 8;
133
+ result.as.a->items = (Value*)realloc(result.as.a->items, result.as.a->capacity * sizeof(Value));
134
+ }
135
+ result.as.a->items[result.as.a->count++] = val_copy(b.as.a->items[i]);
136
+ }
137
+ return result;
138
+ }
139
+ if (a.type == VAL_ARRAY && b.type != VAL_ARRAY) {
140
+ Value result = val_array();
141
+ for (int i = 0; i < a.as.a->count; i++) {
142
+ if (result.as.a->count >= result.as.a->capacity) {
143
+ result.as.a->capacity = result.as.a->capacity ? result.as.a->capacity * 2 : 8;
144
+ result.as.a->items = (Value*)realloc(result.as.a->items, result.as.a->capacity * sizeof(Value));
145
+ }
146
+ result.as.a->items[result.as.a->count++] = val_copy(a.as.a->items[i]);
147
+ }
148
+ if (result.as.a->count >= result.as.a->capacity) {
149
+ result.as.a->capacity = result.as.a->capacity ? result.as.a->capacity * 2 : 8;
150
+ result.as.a->items = (Value*)realloc(result.as.a->items, result.as.a->capacity * sizeof(Value));
151
+ }
152
+ result.as.a->items[result.as.a->count++] = val_copy(b);
153
+ return result;
154
+ }
155
+ if (a.type != VAL_ARRAY && b.type == VAL_ARRAY) {
156
+ Value result = val_array();
157
+ if (result.as.a->count >= result.as.a->capacity) {
158
+ result.as.a->capacity = result.as.a->capacity ? result.as.a->capacity * 2 : 8;
159
+ result.as.a->items = (Value*)realloc(result.as.a->items, result.as.a->capacity * sizeof(Value));
160
+ }
161
+ result.as.a->items[result.as.a->count++] = val_copy(a);
162
+ for (int i = 0; i < b.as.a->count; i++) {
163
+ if (result.as.a->count >= result.as.a->capacity) {
164
+ result.as.a->capacity = result.as.a->capacity ? result.as.a->capacity * 2 : 8;
165
+ result.as.a->items = (Value*)realloc(result.as.a->items, result.as.a->capacity * sizeof(Value));
166
+ }
167
+ result.as.a->items[result.as.a->count++] = val_copy(b.as.a->items[i]);
168
+ }
169
+ return result;
170
+ }
171
+ if (a.type == VAL_STRING || b.type == VAL_STRING) {
172
+ Value sa = val_to_string(a);
173
+ Value sb = val_to_string(b);
174
+ int len = strlen(sa.as.s) + strlen(sb.as.s) + 1;
175
+ char* buf = (char*)malloc(len);
176
+ strcpy(buf, sa.as.s);
177
+ strcat(buf, sb.as.s);
178
+ val_free(sa); val_free(sb);
179
+ Value r; r.type = VAL_STRING; r.as.s = buf;
180
+ return r;
181
+ }
182
+ return val_null();
183
+ }
184
+
185
+ Value val_sub(Value a, Value b) {
186
+ if (a.type == VAL_INT && b.type == VAL_INT) return val_int(a.as.i - b.as.i);
187
+ return val_null();
188
+ }
189
+
190
+ Value val_mul(Value a, Value b) {
191
+ if (a.type == VAL_INT && b.type == VAL_INT) return val_int(a.as.i * b.as.i);
192
+ return val_null();
193
+ }
194
+
195
+ Value val_div(Value a, Value b) {
196
+ if (a.type == VAL_INT && b.type == VAL_INT) {
197
+ if (b.as.i == 0) { fprintf(stderr, "Runtime error: division by zero\n"); exit(1); }
198
+ return val_int(a.as.i / b.as.i);
199
+ }
200
+ return val_null();
201
+ }
202
+
203
+ Value val_mod(Value a, Value b) {
204
+ if (a.type == VAL_INT && b.type == VAL_INT) {
205
+ if (b.as.i == 0) { fprintf(stderr, "Runtime error: modulo by zero\n"); exit(1); }
206
+ return val_int(a.as.i % b.as.i);
207
+ }
208
+ return val_null();
209
+ }
210
+
211
+ int val_equals(Value a, Value b) {
212
+ if (a.type == b.type) {
213
+ switch (a.type) {
214
+ case VAL_NULL: return 1;
215
+ case VAL_INT: return a.as.i == b.as.i;
216
+ case VAL_BOOL: return a.as.b == b.as.b;
217
+ case VAL_STRING: return strcmp(a.as.s, b.as.s) == 0;
218
+ default: return 0;
219
+ }
220
+ }
221
+ // Cross-type: bool <-> int, null <-> int(0)
222
+ if (a.type == VAL_BOOL && b.type == VAL_INT) return a.as.b == (b.as.i != 0);
223
+ if (a.type == VAL_INT && b.type == VAL_BOOL) return (a.as.i != 0) == b.as.b;
224
+ if (a.type == VAL_NULL && b.type == VAL_INT) return b.as.i == 0;
225
+ if (a.type == VAL_INT && b.type == VAL_NULL) return a.as.i == 0;
226
+ return 0;
227
+ }
228
+
229
+ int val_less(Value a, Value b) {
230
+ if (a.type == VAL_INT && b.type == VAL_INT) return a.as.i < b.as.i;
231
+ return 0;
232
+ }
233
+
234
+ int val_greater(Value a, Value b) {
235
+ if (a.type == VAL_INT && b.type == VAL_INT) return a.as.i > b.as.i;
236
+ return 0;
237
+ }
238
+
239
+ // ============================================================================
240
+ // LEXER
241
+ // ============================================================================
242
+
243
+ typedef enum {
244
+ // Literals
245
+ TOK_INT_LIT, TOK_STRING_LIT, TOK_IDENT,
246
+ // Keywords
247
+ TOK_FUNC, TOK_DATA, TOK_RUN, TOK_END,
248
+ TOK_IF, TOK_ELSE, TOK_THEN, TOK_WHILE, TOK_FOR, TOK_TO, TOK_STEP,
249
+ TOK_RETURN, TOK_BREAK, TOK_CONTINUE,
250
+ TOK_PRINT, TOK_LET, TOK_CONST,
251
+ // Operators
252
+ TOK_PLUS, TOK_MINUS, TOK_STAR, TOK_SLASH, TOK_PERCENT,
253
+ TOK_EQ, TOK_NEQ, TOK_LT, TOK_GT, TOK_LTE, TOK_GTE,
254
+ TOK_AND, TOK_OR, TOK_NOT, TOK_ASSIGN,
255
+ TOK_DOT, TOK_COMMA, TOK_COLON, TOK_SEMICOLON,
256
+ TOK_LPAREN, TOK_RPAREN, TOK_LBRACKET, TOK_RBRACKET,
257
+ TOK_ELSEIF, TOK_TRUE, TOK_FALSE, TOK_NULL,
258
+ // Bitwise operators
259
+ TOK_BITAND, TOK_BITOR, TOK_LSHIFT, TOK_RSHIFT,
260
+ // Exception handling keywords
261
+ TOK_TRY, TOK_CATCH, TOK_THROW, TOK_FINALLY, TOK_ENDTRY,
262
+ // Special
263
+ TOK_EOF, TOK_ERROR
264
+ } NbsTokenType;
265
+
266
+ typedef struct {
267
+ NbsTokenType type;
268
+ char text[256];
269
+ int64_t int_val;
270
+ int line;
271
+ } NbsToken;
272
+
273
+ typedef struct {
274
+ const char* src;
275
+ int pos, len, line;
276
+ } Lexer;
277
+
278
+ Lexer lexer_new(const char* src) {
279
+ Lexer l = {src, 0, (int)strlen(src), 1};
280
+ return l;
281
+ }
282
+
283
+ char lexer_peek(Lexer* l) {
284
+ if (l->pos >= l->len) return 0;
285
+ return l->src[l->pos];
286
+ }
287
+
288
+ char lexer_advance(Lexer* l) {
289
+ char c = l->src[l->pos++];
290
+ if (c == '\n') l->line++;
291
+ return c;
292
+ }
293
+
294
+ void lexer_skip_whitespace(Lexer* l) {
295
+ while (l->pos < l->len) {
296
+ char c = l->src[l->pos];
297
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { l->pos++; if (c == '\n') l->line++; }
298
+ else if (c == '#') { while (l->pos < l->len && l->src[l->pos] != '\n') l->pos++; }
299
+ else break;
300
+ }
301
+ }
302
+
303
+ NbsToken lexer_next(Lexer* l) {
304
+ lexer_skip_whitespace(l);
305
+ NbsToken tok = {0};
306
+ tok.line = l->line;
307
+
308
+ if (l->pos >= l->len) { tok.type = TOK_EOF; return tok; }
309
+
310
+ char c = l->src[l->pos];
311
+
312
+ // Numbers
313
+ if (c >= '0' && c <= '9') {
314
+ int64_t val = 0;
315
+ while (l->pos < l->len && l->src[l->pos] >= '0' && l->src[l->pos] <= '9')
316
+ val = val * 10 + lexer_advance(l) - '0';
317
+ tok.type = TOK_INT_LIT;
318
+ tok.int_val = val;
319
+ sprintf(tok.text, "%lld", val);
320
+ return tok;
321
+ }
322
+
323
+ // Strings
324
+ if (c == '"') {
325
+ lexer_advance(l);
326
+ int start = l->pos;
327
+ while (l->pos < l->len && l->src[l->pos] != '"') {
328
+ if (l->src[l->pos] == '\\') l->pos++; // skip escaped char
329
+ l->pos++;
330
+ }
331
+ int slen = l->pos - start;
332
+ if (slen > 255) slen = 255;
333
+ // Copy with escape processing
334
+ int di = 0;
335
+ for (int si = start; si < l->pos && di < 255; si++) {
336
+ if (l->src[si] == '\\' && si + 1 < l->pos) {
337
+ si++;
338
+ if (l->src[si] == 'n') tok.text[di++] = '\n';
339
+ else if (l->src[si] == 't') tok.text[di++] = '\t';
340
+ else if (l->src[si] == '\\') tok.text[di++] = '\\';
341
+ else if (l->src[si] == '"') tok.text[di++] = '"';
342
+ else tok.text[di++] = l->src[si];
343
+ } else {
344
+ tok.text[di++] = l->src[si];
345
+ }
346
+ }
347
+ tok.text[di] = 0;
348
+ tok.type = TOK_STRING_LIT;
349
+ if (l->pos < l->len) lexer_advance(l); // skip closing quote
350
+ return tok;
351
+ }
352
+
353
+ // Identifiers and keywords
354
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') {
355
+ int start = l->pos;
356
+ while (l->pos < l->len && (
357
+ (l->src[l->pos] >= 'a' && l->src[l->pos] <= 'z') ||
358
+ (l->src[l->pos] >= 'A' && l->src[l->pos] <= 'Z') ||
359
+ (l->src[l->pos] >= '0' && l->src[l->pos] <= '9') ||
360
+ l->src[l->pos] == '_' || l->src[l->pos] == '!' || l->src[l->pos] == '?'
361
+ )) l->pos++;
362
+ int slen = l->pos - start;
363
+ memcpy(tok.text, l->src + start, slen);
364
+ tok.text[slen] = 0;
365
+
366
+ // Keywords (Nebulara syntax)
367
+ if (strcmp(tok.text, "FUNC!") == 0) tok.type = TOK_FUNC;
368
+ else if (strcmp(tok.text, "DATA!") == 0) tok.type = TOK_DATA;
369
+ else if (strcmp(tok.text, "RUN!") == 0) tok.type = TOK_RUN;
370
+ else if (strcmp(tok.text, "END!") == 0) tok.type = TOK_END;
371
+ else if (strcmp(tok.text, "IF?") == 0) tok.type = TOK_IF;
372
+ else if (strcmp(tok.text, "ELSE") == 0) tok.type = TOK_ELSE;
373
+ else if (strcmp(tok.text, "WHILE?") == 0) tok.type = TOK_WHILE;
374
+ else if (strcmp(tok.text, "FOR!") == 0) tok.type = TOK_FOR;
375
+ else if (strcmp(tok.text, "THEN") == 0) tok.type = TOK_THEN;
376
+ else if (strcmp(tok.text, "TO") == 0) tok.type = TOK_TO;
377
+ else if (strcmp(tok.text, "STEP") == 0) tok.type = TOK_STEP;
378
+ else if (strcmp(tok.text, "RETURN") == 0) tok.type = TOK_RETURN;
379
+ else if (strcmp(tok.text, "BREAK") == 0) tok.type = TOK_BREAK;
380
+ else if (strcmp(tok.text, "CONTINUE") == 0) tok.type = TOK_CONTINUE;
381
+ else if (strcmp(tok.text, "PRINT") == 0) tok.type = TOK_PRINT;
382
+ else if (strcmp(tok.text, "LET") == 0) tok.type = TOK_LET;
383
+ else if (strcmp(tok.text, "CONST") == 0) tok.type = TOK_CONST;
384
+ else if (strcmp(tok.text, "AND") == 0) tok.type = TOK_AND;
385
+ else if (strcmp(tok.text, "OR") == 0) tok.type = TOK_OR;
386
+ else if (strcmp(tok.text, "NOT") == 0) tok.type = TOK_NOT;
387
+ else if (strcmp(tok.text, "NULL") == 0) { tok.type = TOK_NULL; strcpy(tok.text, "null"); }
388
+ else if (strcmp(tok.text, "TRUE") == 0) { tok.type = TOK_TRUE; strcpy(tok.text, "true"); }
389
+ else if (strcmp(tok.text, "FALSE") == 0) { tok.type = TOK_FALSE; strcpy(tok.text, "false"); }
390
+ else if (strcmp(tok.text, "ELSEIF?") == 0) tok.type = TOK_ELSEIF;
391
+ else if (strcmp(tok.text, "TRY!") == 0) tok.type = TOK_TRY;
392
+ else if (strcmp(tok.text, "CATCH!") == 0) tok.type = TOK_CATCH;
393
+ else if (strcmp(tok.text, "THROW") == 0) tok.type = TOK_THROW;
394
+ else if (strcmp(tok.text, "FINALLY!") == 0) tok.type = TOK_FINALLY;
395
+ else if (strcmp(tok.text, "ENDTRY!") == 0) tok.type = TOK_ENDTRY;
396
+ else tok.type = TOK_IDENT;
397
+ return tok;
398
+ }
399
+
400
+ // Operators
401
+ lexer_advance(l);
402
+ tok.text[0] = c; tok.text[1] = 0;
403
+
404
+ if (c == '+') tok.type = TOK_PLUS;
405
+ else if (c == '-') tok.type = TOK_MINUS;
406
+ else if (c == '*') tok.type = TOK_STAR;
407
+ else if (c == '/') tok.type = TOK_SLASH;
408
+ else if (c == '%') tok.type = TOK_PERCENT;
409
+ else if (c == '=') {
410
+ if (lexer_peek(l) == '=') { lexer_advance(l); tok.text[1] = '='; tok.text[2] = 0; tok.type = TOK_EQ; }
411
+ else tok.type = TOK_ASSIGN;
412
+ }
413
+ else if (c == '!') {
414
+ if (lexer_peek(l) == '=') { lexer_advance(l); tok.text[1] = '='; tok.text[2] = 0; tok.type = TOK_NEQ; }
415
+ else { tok.type = TOK_ERROR; }
416
+ }
417
+ else if (c == '<') {
418
+ if (lexer_peek(l) == '=') { lexer_advance(l); tok.text[1] = '='; tok.text[2] = 0; tok.type = TOK_LTE; }
419
+ else if (lexer_peek(l) == '<') { lexer_advance(l); tok.text[1] = '<'; tok.text[2] = 0; tok.type = TOK_LSHIFT; }
420
+ else tok.type = TOK_LT;
421
+ }
422
+ else if (c == '>') {
423
+ if (lexer_peek(l) == '=') { lexer_advance(l); tok.text[1] = '='; tok.text[2] = 0; tok.type = TOK_GTE; }
424
+ else if (lexer_peek(l) == '>') { lexer_advance(l); tok.text[1] = '>'; tok.text[2] = 0; tok.type = TOK_RSHIFT; }
425
+ else tok.type = TOK_GT;
426
+ }
427
+ else if (c == '&') tok.type = TOK_BITAND;
428
+ else if (c == '|') tok.type = TOK_BITOR;
429
+ else if (c == '(') tok.type = TOK_LPAREN;
430
+ else if (c == ')') tok.type = TOK_RPAREN;
431
+ else if (c == '[') tok.type = TOK_LBRACKET;
432
+ else if (c == ']') tok.type = TOK_RBRACKET;
433
+ else if (c == '.') tok.type = TOK_DOT;
434
+ else if (c == ',') tok.type = TOK_COMMA;
435
+ else if (c == ':') tok.type = TOK_COLON;
436
+ else if (c == ';') tok.type = TOK_SEMICOLON;
437
+ else tok.type = TOK_ERROR;
438
+
439
+ return tok;
440
+ }
441
+
442
+ // ============================================================================
443
+ // AST NODES
444
+ // ============================================================================
445
+
446
+ typedef enum {
447
+ NODE_INT, NODE_STRING, NODE_IDENT, NODE_BINARY, NODE_UNARY,
448
+ NODE_TRUE, NODE_FALSE, NODE_NULL,
449
+ NODE_ASSIGN, NODE_PRINT, NODE_BLOCK, NODE_IF, NODE_WHILE, NODE_FOR,
450
+ NODE_FUNC_DEF, NODE_FUNC_CALL, NODE_RETURN, NODE_BREAK, NODE_CONTINUE,
451
+ NODE_ARRAY_LIT, NODE_ARRAY_INDEX, NODE_ARRAY_LEN, NODE_ARRAY_ASSIGN,
452
+ NODE_LEN, NODE_TYPEOF, NODE_TOSTR, NODE_TONUM,
453
+ NODE_TRY, NODE_THROW,
454
+ NODE_PROGRAM
455
+ } NodeType;
456
+
457
+ typedef struct ASTNode ASTNode;
458
+
459
+ struct ASTNode {
460
+ NodeType type;
461
+ int64_t int_val;
462
+ char str_val[256];
463
+ char op[8];
464
+ ASTNode* left;
465
+ ASTNode* right;
466
+ ASTNode* third; // for IF (else branch)
467
+ ASTNode** children; // for blocks
468
+ int child_count;
469
+ char params[8][128]; // function parameter names
470
+ int param_count;
471
+ int line;
472
+ };
473
+
474
+ ASTNode* ast_new(NodeType type) {
475
+ ASTNode* n = (ASTNode*)calloc(1, sizeof(ASTNode));
476
+ n->type = type;
477
+ return n;
478
+ }
479
+
480
+ // ============================================================================
481
+ // PARSER — Recursive descent
482
+ // ============================================================================
483
+
484
+ typedef struct {
485
+ NbsToken tokens[4096];
486
+ int pos, count;
487
+ int has_error;
488
+ char error_msg[512];
489
+ } Parser;
490
+
491
+ void parser_error(Parser* p, const char* msg, int line) {
492
+ if (!p->has_error) {
493
+ p->has_error = 1;
494
+ snprintf(p->error_msg, sizeof(p->error_msg), "Line %d: %s", line, msg);
495
+ }
496
+ }
497
+
498
+ NbsToken parser_peek(Parser* p) {
499
+ if (p->pos >= p->count) return (NbsToken){TOK_EOF};
500
+ return p->tokens[p->pos];
501
+ }
502
+
503
+ NbsToken parser_advance(Parser* p) {
504
+ return p->tokens[p->pos++];
505
+ }
506
+
507
+ NbsToken parser_expect(Parser* p, NbsTokenType type) {
508
+ NbsToken tok = parser_peek(p);
509
+ if (tok.type != type) {
510
+ char buf[256];
511
+ snprintf(buf, sizeof(buf), "Expected NbsToken type %d, got '%s'", type, tok.text);
512
+ parser_error(p, buf, tok.line);
513
+ }
514
+ return parser_advance(p);
515
+ }
516
+
517
+ // Expression parsing (Pratt parser for precedence)
518
+ int get_precedence(NbsTokenType t) {
519
+ switch (t) {
520
+ case TOK_OR: return 1;
521
+ case TOK_AND: return 2;
522
+ case TOK_BITOR: return 3;
523
+ case TOK_BITAND: return 4;
524
+ case TOK_EQ: case TOK_NEQ: return 5;
525
+ case TOK_LT: case TOK_GT: case TOK_LTE: case TOK_GTE: case TOK_LSHIFT: case TOK_RSHIFT: return 6;
526
+ case TOK_PLUS: case TOK_MINUS: return 7;
527
+ case TOK_STAR: case TOK_SLASH: case TOK_PERCENT: return 8;
528
+ case TOK_NOT: return 9;
529
+ default: return 0;
530
+ }
531
+ }
532
+
533
+ ASTNode* parse_expression(Parser* p);
534
+
535
+ ASTNode* parse_primary(Parser* p) {
536
+ NbsToken tok = parser_peek(p);
537
+
538
+ if (tok.type == TOK_INT_LIT) {
539
+ parser_advance(p);
540
+ ASTNode* n = ast_new(NODE_INT);
541
+ n->int_val = tok.int_val;
542
+ strcpy(n->str_val, tok.text);
543
+ n->line = tok.line;
544
+ return n;
545
+ }
546
+
547
+ if (tok.type == TOK_TRUE) {
548
+ parser_advance(p);
549
+ ASTNode* n = ast_new(NODE_TRUE);
550
+ n->line = tok.line;
551
+ return n;
552
+ }
553
+ if (tok.type == TOK_FALSE) {
554
+ parser_advance(p);
555
+ ASTNode* n = ast_new(NODE_FALSE);
556
+ n->line = tok.line;
557
+ return n;
558
+ }
559
+ if (tok.type == TOK_NULL) {
560
+ parser_advance(p);
561
+ ASTNode* n = ast_new(NODE_NULL);
562
+ n->line = tok.line;
563
+ return n;
564
+ }
565
+
566
+ if (tok.type == TOK_STRING_LIT) {
567
+ parser_advance(p);
568
+ ASTNode* n = ast_new(NODE_STRING);
569
+ strcpy(n->str_val, tok.text);
570
+ n->line = tok.line;
571
+ return n;
572
+ }
573
+
574
+ if (tok.type == TOK_IDENT) {
575
+ parser_advance(p);
576
+ // Check for array index: ident[expr]
577
+ if (parser_peek(p).type == TOK_LBRACKET) {
578
+ parser_advance(p);
579
+ ASTNode* idx = parse_expression(p);
580
+ parser_expect(p, TOK_RBRACKET);
581
+ ASTNode* n = ast_new(NODE_ARRAY_INDEX);
582
+ ASTNode* id = ast_new(NODE_IDENT);
583
+ strcpy(id->str_val, tok.text);
584
+ n->left = id;
585
+ n->right = idx;
586
+ n->line = tok.line;
587
+ return n;
588
+ }
589
+ // Check for function call: ident(args)
590
+ if (parser_peek(p).type == TOK_LPAREN) {
591
+ parser_advance(p);
592
+ ASTNode* n = ast_new(NODE_FUNC_CALL);
593
+ strcpy(n->str_val, tok.text);
594
+ n->children = NULL;
595
+ n->child_count = 0;
596
+ if (parser_peek(p).type != TOK_RPAREN) {
597
+ int cap = 8;
598
+ n->children = (ASTNode**)malloc(cap * sizeof(ASTNode*));
599
+ do {
600
+ if (n->child_count >= cap) {
601
+ cap *= 2;
602
+ n->children = (ASTNode**)realloc(n->children, cap * sizeof(ASTNode*));
603
+ }
604
+ n->children[n->child_count++] = parse_expression(p);
605
+ } while (parser_peek(p).type == TOK_COMMA && (parser_advance(p), 1));
606
+ }
607
+ parser_expect(p, TOK_RPAREN);
608
+ n->line = tok.line;
609
+ return n;
610
+ }
611
+ // Built-in functions
612
+ if (strcmp(tok.text, "LEN") == 0 && parser_peek(p).type == TOK_LPAREN) {
613
+ parser_advance(p);
614
+ ASTNode* n = ast_new(NODE_ARRAY_LEN);
615
+ n->left = parse_expression(p);
616
+ parser_expect(p, TOK_RPAREN);
617
+ n->line = tok.line;
618
+ return n;
619
+ }
620
+ if (strcmp(tok.text, "TYPEOF") == 0 && parser_peek(p).type == TOK_LPAREN) {
621
+ parser_advance(p);
622
+ ASTNode* n = ast_new(NODE_TYPEOF);
623
+ n->left = parse_expression(p);
624
+ parser_expect(p, TOK_RPAREN);
625
+ n->line = tok.line;
626
+ return n;
627
+ }
628
+ if (strcmp(tok.text, "TO_STRING") == 0 && parser_peek(p).type == TOK_LPAREN) {
629
+ parser_advance(p);
630
+ ASTNode* n = ast_new(NODE_TOSTR);
631
+ n->left = parse_expression(p);
632
+ parser_expect(p, TOK_RPAREN);
633
+ n->line = tok.line;
634
+ return n;
635
+ }
636
+ if (strcmp(tok.text, "TO_NUMBER") == 0 && parser_peek(p).type == TOK_LPAREN) {
637
+ parser_advance(p);
638
+ ASTNode* n = ast_new(NODE_TONUM);
639
+ n->left = parse_expression(p);
640
+ parser_expect(p, TOK_RPAREN);
641
+ n->line = tok.line;
642
+ return n;
643
+ }
644
+ ASTNode* n = ast_new(NODE_IDENT);
645
+ strcpy(n->str_val, tok.text);
646
+ n->line = tok.line;
647
+ return n;
648
+ }
649
+
650
+ if (tok.type == TOK_LPAREN) {
651
+ parser_advance(p);
652
+ ASTNode* expr = parse_expression(p);
653
+ parser_expect(p, TOK_RPAREN);
654
+ return expr;
655
+ }
656
+
657
+ if (tok.type == TOK_LBRACKET) {
658
+ parser_advance(p);
659
+ ASTNode* n = ast_new(NODE_ARRAY_LIT);
660
+ n->children = NULL;
661
+ n->child_count = 0;
662
+ int cap = 8;
663
+ n->children = (ASTNode**)malloc(cap * sizeof(ASTNode*));
664
+ if (parser_peek(p).type != TOK_RBRACKET) {
665
+ do {
666
+ if (n->child_count >= cap) {
667
+ cap *= 2;
668
+ n->children = (ASTNode**)realloc(n->children, cap * sizeof(ASTNode*));
669
+ }
670
+ n->children[n->child_count++] = parse_expression(p);
671
+ } while (parser_peek(p).type == TOK_COMMA && (parser_advance(p), 1));
672
+ }
673
+ parser_expect(p, TOK_RBRACKET);
674
+ n->line = tok.line;
675
+ return n;
676
+ }
677
+
678
+ if (tok.type == TOK_MINUS) {
679
+ parser_advance(p);
680
+ ASTNode* n = ast_new(NODE_UNARY);
681
+ strcpy(n->op, "-");
682
+ n->left = parse_primary(p);
683
+ n->line = tok.line;
684
+ return n;
685
+ }
686
+
687
+ if (tok.type == TOK_NOT) {
688
+ parser_advance(p);
689
+ ASTNode* n = ast_new(NODE_UNARY);
690
+ strcpy(n->op, "NOT");
691
+ n->left = parse_primary(p);
692
+ n->line = tok.line;
693
+ return n;
694
+ }
695
+
696
+ parser_error(p, "Unexpected NbsToken in expression", tok.line);
697
+ parser_advance(p);
698
+ return ast_new(NODE_INT);
699
+ }
700
+
701
+ ASTNode* parse_expression_bp(Parser* p, int min_prec) {
702
+ ASTNode* left = parse_primary(p);
703
+
704
+ while (get_precedence(parser_peek(p).type) >= min_prec) {
705
+ NbsToken op = parser_advance(p);
706
+ int prec = get_precedence(op.type);
707
+ ASTNode* right = parse_expression_bp(p, prec + 1);
708
+
709
+ ASTNode* bin = ast_new(NODE_BINARY);
710
+ strcpy(bin->op, op.text);
711
+ bin->left = left;
712
+ bin->right = right;
713
+ bin->line = op.line;
714
+ left = bin;
715
+ }
716
+
717
+ return left;
718
+ }
719
+
720
+ ASTNode* parse_expression(Parser* p) {
721
+ return parse_expression_bp(p, 1);
722
+ }
723
+
724
+ // Statement parsing
725
+ ASTNode* parse_statement(Parser* p);
726
+ ASTNode* parse_block(Parser* p);
727
+
728
+ ASTNode* parse_block(Parser* p) {
729
+ ASTNode* block = ast_new(NODE_BLOCK);
730
+ int cap = 16;
731
+ block->children = (ASTNode**)malloc(cap * sizeof(ASTNode*));
732
+ block->child_count = 0;
733
+
734
+ while (parser_peek(p).type != TOK_END && parser_peek(p).type != TOK_EOF &&
735
+ parser_peek(p).type != TOK_ELSE && parser_peek(p).type != TOK_ELSEIF &&
736
+ parser_peek(p).type != TOK_CATCH && parser_peek(p).type != TOK_FINALLY &&
737
+ parser_peek(p).type != TOK_ENDTRY) {
738
+ if (block->child_count >= cap) {
739
+ cap *= 2;
740
+ block->children = (ASTNode**)realloc(block->children, cap * sizeof(ASTNode*));
741
+ }
742
+ block->children[block->child_count++] = parse_statement(p);
743
+ }
744
+ return block;
745
+ }
746
+
747
+ ASTNode* parse_statement(Parser* p) {
748
+ NbsToken tok = parser_peek(p);
749
+
750
+ // PRINT expr
751
+ if (tok.type == TOK_PRINT) {
752
+ parser_advance(p);
753
+ ASTNode* n = ast_new(NODE_PRINT);
754
+ n->left = parse_expression(p);
755
+ n->line = tok.line;
756
+ return n;
757
+ }
758
+
759
+ // LET ident = expr or ident = expr
760
+ if (tok.type == TOK_LET || tok.type == TOK_CONST ||
761
+ (tok.type == TOK_IDENT && p->pos + 1 < p->count && p->tokens[p->pos + 1].type == TOK_ASSIGN)) {
762
+ if (tok.type == TOK_LET || tok.type == TOK_CONST) parser_advance(p);
763
+ NbsToken name = parser_expect(p, TOK_IDENT);
764
+ parser_expect(p, TOK_ASSIGN);
765
+ ASTNode* n = ast_new(NODE_ASSIGN);
766
+ strcpy(n->str_val, name.text);
767
+ n->right = parse_expression(p);
768
+ n->line = tok.line;
769
+ return n;
770
+ }
771
+
772
+ // IF? expr THEN: block (ELSEIF? expr: block)* (ELSE: block) END!
773
+ if (tok.type == TOK_IF) {
774
+ parser_advance(p);
775
+ ASTNode* root = ast_new(NODE_IF);
776
+ ASTNode* current = root;
777
+ root->left = parse_expression(p);
778
+ if (parser_peek(p).type == TOK_THEN) parser_advance(p);
779
+ parser_expect(p, TOK_COLON);
780
+ root->right = parse_block(p);
781
+ // Handle ELSEIF? chains: each ELSEIF becomes a nested IF in the third branch
782
+ while (parser_peek(p).type == TOK_ELSEIF) {
783
+ parser_advance(p);
784
+ ASTNode* elseif = ast_new(NODE_IF);
785
+ elseif->left = parse_expression(p);
786
+ if (parser_peek(p).type == TOK_THEN) parser_advance(p);
787
+ parser_expect(p, TOK_COLON);
788
+ elseif->right = parse_block(p);
789
+ current->third = elseif;
790
+ current = elseif;
791
+ }
792
+ if (parser_peek(p).type == TOK_ELSE) {
793
+ parser_advance(p);
794
+ if (parser_peek(p).type == TOK_COLON) parser_advance(p);
795
+ current->third = parse_block(p);
796
+ }
797
+ parser_expect(p, TOK_END);
798
+ root->line = tok.line;
799
+ return root;
800
+ }
801
+
802
+ // WHILE? expr: block END!
803
+ if (tok.type == TOK_WHILE) {
804
+ parser_advance(p);
805
+ ASTNode* n = ast_new(NODE_WHILE);
806
+ n->left = parse_expression(p);
807
+ if (parser_peek(p).type == TOK_THEN) parser_advance(p); // skip optional THEN
808
+ parser_expect(p, TOK_COLON);
809
+ n->right = parse_block(p);
810
+ parser_expect(p, TOK_END);
811
+ n->line = tok.line;
812
+ return n;
813
+ }
814
+
815
+ // FOR! var = start TO end (STEP step): block END!
816
+ if (tok.type == TOK_FOR) {
817
+ parser_advance(p);
818
+ NbsToken var = parser_expect(p, TOK_IDENT);
819
+ parser_expect(p, TOK_ASSIGN);
820
+ ASTNode* start = parse_expression(p);
821
+ parser_expect(p, TOK_TO);
822
+ ASTNode* end = parse_expression(p);
823
+ ASTNode* step = NULL;
824
+ if (parser_peek(p).type == TOK_STEP) {
825
+ parser_advance(p);
826
+ step = parse_expression(p);
827
+ }
828
+ parser_expect(p, TOK_COLON);
829
+ ASTNode* body = parse_block(p);
830
+ parser_expect(p, TOK_END);
831
+
832
+ ASTNode* n = ast_new(NODE_FOR);
833
+ strcpy(n->str_val, var.text);
834
+ ASTNode* range = ast_new(NODE_BINARY);
835
+ strcpy(range->op, "TO");
836
+ range->left = start;
837
+ range->right = end;
838
+ if (step) {
839
+ ASTNode* stepnode = ast_new(NODE_BINARY);
840
+ strcpy(stepnode->op, "STEP");
841
+ stepnode->left = range;
842
+ stepnode->right = step;
843
+ n->left = stepnode;
844
+ } else {
845
+ n->left = range;
846
+ }
847
+ n->right = body;
848
+ n->line = tok.line;
849
+ return n;
850
+ }
851
+
852
+ // FUNC! name param1 param2: block END!
853
+ if (tok.type == TOK_FUNC) {
854
+ parser_advance(p);
855
+ NbsToken name = parser_expect(p, TOK_IDENT);
856
+ ASTNode* n = ast_new(NODE_FUNC_DEF);
857
+ strcpy(n->str_val, name.text);
858
+ n->param_count = 0;
859
+ // Collect parameter names until colon
860
+ while (parser_peek(p).type != TOK_COLON && parser_peek(p).type != TOK_EOF) {
861
+ NbsToken param = parser_advance(p);
862
+ if (param.type == TOK_IDENT) {
863
+ strcpy(n->params[n->param_count++], param.text);
864
+ }
865
+ }
866
+ parser_expect(p, TOK_COLON);
867
+ ASTNode* body = parse_block(p);
868
+ parser_expect(p, TOK_END);
869
+ n->right = body;
870
+ n->line = tok.line;
871
+ return n;
872
+ }
873
+
874
+ // RETURN expr
875
+ if (tok.type == TOK_RETURN) {
876
+ parser_advance(p);
877
+ ASTNode* n = ast_new(NODE_RETURN);
878
+ if (parser_peek(p).type != TOK_END && parser_peek(p).type != TOK_EOF &&
879
+ parser_peek(p).type != TOK_ELSE && parser_peek(p).type != TOK_ELSEIF) {
880
+ n->left = parse_expression(p);
881
+ }
882
+ n->line = tok.line;
883
+ return n;
884
+ }
885
+
886
+ if (tok.type == TOK_BREAK) { parser_advance(p); ASTNode* n = ast_new(NODE_BREAK); n->line = tok.line; return n; }
887
+ if (tok.type == TOK_CONTINUE) { parser_advance(p); ASTNode* n = ast_new(NODE_CONTINUE); n->line = tok.line; return n; }
888
+
889
+ // THROW expr
890
+ if (tok.type == TOK_THROW) {
891
+ parser_advance(p);
892
+ ASTNode* n = ast_new(NODE_THROW);
893
+ n->left = parse_expression(p);
894
+ n->line = tok.line;
895
+ return n;
896
+ }
897
+
898
+ // TRY!: block CATCH! var: block (FINALLY!: block)? ENDTRY!
899
+ if (tok.type == TOK_TRY) {
900
+ parser_advance(p);
901
+ if (parser_peek(p).type == TOK_COLON) parser_advance(p);
902
+ ASTNode* n = ast_new(NODE_TRY);
903
+ n->right = parse_block(p); // try block
904
+ if (parser_peek(p).type == TOK_CATCH) {
905
+ parser_advance(p);
906
+ NbsToken errvar = parser_peek(p);
907
+ if (errvar.type == TOK_IDENT) {
908
+ parser_advance(p);
909
+ strcpy(n->str_val, errvar.text); // error variable name
910
+ }
911
+ if (parser_peek(p).type == TOK_COLON) parser_advance(p);
912
+ n->third = parse_block(p); // catch block
913
+ }
914
+ if (parser_peek(p).type == TOK_FINALLY) {
915
+ parser_advance(p);
916
+ if (parser_peek(p).type == TOK_COLON) parser_advance(p);
917
+ // finally block — we append it to catch block for simplicity
918
+ ASTNode* finally_block = parse_block(p);
919
+ if (n->third) {
920
+ // Append finally statements to catch block
921
+ ASTNode* catch_blk = n->third;
922
+ for (int i = 0; i < finally_block->child_count; i++) {
923
+ if (catch_blk->child_count >= 16) break;
924
+ catch_blk->children[catch_blk->child_count++] = finally_block->children[i];
925
+ }
926
+ } else {
927
+ n->third = finally_block;
928
+ }
929
+ }
930
+ parser_expect(p, TOK_ENDTRY);
931
+ n->line = tok.line;
932
+ return n;
933
+ }
934
+
935
+ // Expression statement (may be array assignment: arr[i] = expr)
936
+ ASTNode* expr = parse_expression(p);
937
+ // Check for array assignment: expr[i] = value
938
+ if (expr->type == NODE_ARRAY_INDEX && parser_peek(p).type == TOK_ASSIGN) {
939
+ parser_advance(p);
940
+ ASTNode* n = ast_new(NODE_ARRAY_ASSIGN);
941
+ n->left = expr; // NODE_ARRAY_INDEX (left=array, right=index)
942
+ n->right = parse_expression(p);
943
+ n->line = tok.line;
944
+ return n;
945
+ }
946
+ return expr;
947
+ }
948
+
949
+ ASTNode* parse_program(Parser* p) {
950
+ ASTNode* prog = ast_new(NODE_PROGRAM);
951
+ int cap = 64;
952
+ prog->children = (ASTNode**)malloc(cap * sizeof(ASTNode*));
953
+ prog->child_count = 0;
954
+
955
+ while (parser_peek(p).type != TOK_EOF) {
956
+ if (prog->child_count >= cap) {
957
+ cap *= 2;
958
+ prog->children = (ASTNode**)realloc(prog->children, cap * sizeof(ASTNode*));
959
+ }
960
+ prog->children[prog->child_count++] = parse_statement(p);
961
+ }
962
+ return prog;
963
+ }
964
+
965
+ // ============================================================================
966
+ // BYTECODE COMPILER
967
+ // ============================================================================
968
+
969
+ typedef enum {
970
+ BC_PUSH_INT, BC_PUSH_STR, BC_PUSH_BOOL, BC_PUSH_NULL,
971
+ BC_POP, BC_DUP, BC_SWAP,
972
+ BC_ADD, BC_SUB, BC_MUL, BC_DIV, BC_MOD, BC_NEG,
973
+ BC_EQ, BC_NEQ, BC_LT, BC_GT, BC_LTE, BC_GTE,
974
+ BC_AND, BC_OR, BC_NOT,
975
+ BC_BITAND, BC_BITOR, BC_LSHIFT, BC_RSHIFT,
976
+ BC_STORE, BC_LOAD,
977
+ BC_JUMP, BC_JUMP_IF, BC_JUMP_IFNOT,
978
+ BC_CALL, BC_RET,
979
+ BC_PRINT, BC_ARRAY_NEW, BC_ARRAY_GET, BC_ARRAY_SET, BC_ARRAY_LEN, BC_ARRAY_PUSH, BC_ARRAY_POP,
980
+ BC_LEN, BC_TYPEOF, BC_TOSTR, BC_TONUM,
981
+ BC_TRY, BC_CATCH, BC_THROW, BC_ENDTRY,
982
+ BC_HALT
983
+ } BCOp;
984
+
985
+ typedef struct {
986
+ uint8_t* code;
987
+ int pos, cap;
988
+ } BytecodeBuf;
989
+
990
+ void bc_init(BytecodeBuf* b) {
991
+ b->cap = 65536;
992
+ b->code = (uint8_t*)malloc(b->cap);
993
+ b->pos = 0;
994
+ }
995
+
996
+ void bc_emit(BytecodeBuf* b, uint8_t byte) {
997
+ if (b->pos >= b->cap) {
998
+ b->cap *= 2;
999
+ b->code = (uint8_t*)realloc(b->code, b->cap);
1000
+ }
1001
+ b->code[b->pos++] = byte;
1002
+ }
1003
+
1004
+ void bc_emit_i64(BytecodeBuf* b, int64_t v) {
1005
+ for (int i = 0; i < 8; i++) bc_emit(b, (v >> (i * 8)) & 0xFF);
1006
+ }
1007
+
1008
+ void bc_emit_i32(BytecodeBuf* b, int32_t v) {
1009
+ for (int i = 0; i < 4; i++) bc_emit(b, (v >> (i * 8)) & 0xFF);
1010
+ }
1011
+
1012
+ int bc_patch(BytecodeBuf* b, int addr) {
1013
+ int offset = b->pos - addr - 4;
1014
+ b->code[addr] = offset & 0xFF;
1015
+ b->code[addr + 1] = (offset >> 8) & 0xFF;
1016
+ b->code[addr + 2] = (offset >> 16) & 0xFF;
1017
+ b->code[addr + 3] = (offset >> 24) & 0xFF;
1018
+ return offset;
1019
+ }
1020
+
1021
+ // String table
1022
+ typedef struct {
1023
+ char** strings;
1024
+ int count, cap;
1025
+ } StringTable;
1026
+
1027
+ void st_init(StringTable* st) {
1028
+ st->cap = 256;
1029
+ st->strings = (char**)malloc(st->cap * sizeof(char*));
1030
+ st->count = 0;
1031
+ }
1032
+
1033
+ int st_intern(StringTable* st, const char* s) {
1034
+ for (int i = 0; i < st->count; i++)
1035
+ if (strcmp(st->strings[i], s) == 0) return i;
1036
+ if (st->count >= st->cap) {
1037
+ st->cap *= 2;
1038
+ st->strings = (char**)realloc(st->strings, st->cap * sizeof(char*));
1039
+ }
1040
+ st->strings[st->count] = strdup(s);
1041
+ return st->count++;
1042
+ }
1043
+
1044
+ // Function table
1045
+ typedef struct {
1046
+ char name[128];
1047
+ int addr;
1048
+ int arity;
1049
+ char params[8][128];
1050
+ int param_count;
1051
+ } FuncEntry;
1052
+
1053
+ typedef struct {
1054
+ FuncEntry* entries;
1055
+ int count, cap;
1056
+ } FuncTable;
1057
+
1058
+ void ft_init(FuncTable* ft) {
1059
+ ft->cap = 64;
1060
+ ft->entries = (FuncEntry*)malloc(ft->cap * sizeof(FuncEntry));
1061
+ ft->count = 0;
1062
+ }
1063
+
1064
+ int ft_add(FuncTable* ft, const char* name, int addr, char params[][128], int param_count) {
1065
+ if (ft->count >= ft->cap) {
1066
+ ft->cap *= 2;
1067
+ ft->entries = (FuncEntry*)realloc(ft->entries, ft->cap * sizeof(FuncEntry));
1068
+ }
1069
+ FuncEntry* e = &ft->entries[ft->count];
1070
+ strcpy(e->name, name);
1071
+ e->addr = addr;
1072
+ e->arity = param_count;
1073
+ e->param_count = param_count;
1074
+ for (int i = 0; i < param_count; i++) strcpy(e->params[i], params[i]);
1075
+ return ft->count++;
1076
+ }
1077
+
1078
+ int ft_find(FuncTable* ft, const char* name) {
1079
+ for (int i = 0; i < ft->count; i++)
1080
+ if (strcmp(ft->entries[i].name, name) == 0) return i;
1081
+ return -1;
1082
+ }
1083
+
1084
+ // Loop break/continue patch stack
1085
+ typedef struct { int break_patches[64]; int break_count; int continue_patches[64]; int continue_count; int continue_ip; } LoopInfo;
1086
+ static LoopInfo loop_stack[64];
1087
+ static int loop_sp = 0;
1088
+
1089
+ // Compiler state
1090
+ typedef struct {
1091
+ BytecodeBuf* bc;
1092
+ StringTable* strings;
1093
+ FuncTable* funcs;
1094
+ } Compiler;
1095
+
1096
+ void compile_node(Compiler* c, ASTNode* node) {
1097
+ if (!node) return;
1098
+
1099
+ switch (node->type) {
1100
+ case NODE_INT:
1101
+ bc_emit(c->bc, BC_PUSH_INT);
1102
+ bc_emit_i64(c->bc, node->int_val);
1103
+ break;
1104
+
1105
+ case NODE_TRUE:
1106
+ bc_emit(c->bc, BC_PUSH_BOOL);
1107
+ bc_emit(c->bc, 1);
1108
+ break;
1109
+
1110
+ case NODE_FALSE:
1111
+ bc_emit(c->bc, BC_PUSH_BOOL);
1112
+ bc_emit(c->bc, 0);
1113
+ break;
1114
+
1115
+ case NODE_NULL:
1116
+ bc_emit(c->bc, BC_PUSH_NULL);
1117
+ break;
1118
+
1119
+ case NODE_STRING: {
1120
+ bc_emit(c->bc, BC_PUSH_STR);
1121
+ int idx = st_intern(c->strings, node->str_val);
1122
+ bc_emit_i32(c->bc, idx);
1123
+ } break;
1124
+
1125
+ case NODE_IDENT:
1126
+ bc_emit(c->bc, BC_LOAD);
1127
+ bc_emit_i32(c->bc, st_intern(c->strings, node->str_val));
1128
+ break;
1129
+
1130
+ case NODE_BINARY:
1131
+ compile_node(c, node->left);
1132
+ compile_node(c, node->right);
1133
+ if (strcmp(node->op, "+") == 0) bc_emit(c->bc, BC_ADD);
1134
+ else if (strcmp(node->op, "-") == 0) bc_emit(c->bc, BC_SUB);
1135
+ else if (strcmp(node->op, "*") == 0) bc_emit(c->bc, BC_MUL);
1136
+ else if (strcmp(node->op, "/") == 0) bc_emit(c->bc, BC_DIV);
1137
+ else if (strcmp(node->op, "%") == 0) bc_emit(c->bc, BC_MOD);
1138
+ else if (strcmp(node->op, "==") == 0) bc_emit(c->bc, BC_EQ);
1139
+ else if (strcmp(node->op, "!=") == 0) bc_emit(c->bc, BC_NEQ);
1140
+ else if (strcmp(node->op, "<") == 0) bc_emit(c->bc, BC_LT);
1141
+ else if (strcmp(node->op, ">") == 0) bc_emit(c->bc, BC_GT);
1142
+ else if (strcmp(node->op, "<=") == 0) bc_emit(c->bc, BC_LTE);
1143
+ else if (strcmp(node->op, ">=") == 0) bc_emit(c->bc, BC_GTE);
1144
+ else if (strcmp(node->op, "AND") == 0) bc_emit(c->bc, BC_AND);
1145
+ else if (strcmp(node->op, "OR") == 0) bc_emit(c->bc, BC_OR);
1146
+ else if (strcmp(node->op, "&") == 0) bc_emit(c->bc, BC_BITAND);
1147
+ else if (strcmp(node->op, "|") == 0) bc_emit(c->bc, BC_BITOR);
1148
+ else if (strcmp(node->op, "<<") == 0) bc_emit(c->bc, BC_LSHIFT);
1149
+ else if (strcmp(node->op, ">>") == 0) bc_emit(c->bc, BC_RSHIFT);
1150
+ break;
1151
+
1152
+ case NODE_UNARY:
1153
+ compile_node(c, node->left);
1154
+ if (strcmp(node->op, "-") == 0) bc_emit(c->bc, BC_NEG);
1155
+ else if (strcmp(node->op, "NOT") == 0) bc_emit(c->bc, BC_NOT);
1156
+ break;
1157
+
1158
+ case NODE_ASSIGN:
1159
+ compile_node(c, node->right);
1160
+ bc_emit(c->bc, BC_STORE);
1161
+ bc_emit_i32(c->bc, st_intern(c->strings, node->str_val));
1162
+ break;
1163
+
1164
+ case NODE_PRINT:
1165
+ compile_node(c, node->left);
1166
+ bc_emit(c->bc, BC_PRINT);
1167
+ break;
1168
+
1169
+ case NODE_IF:
1170
+ compile_node(c, node->left);
1171
+ bc_emit(c->bc, BC_JUMP_IFNOT);
1172
+ int patch1 = c->bc->pos;
1173
+ bc_emit_i32(c->bc, 0);
1174
+ compile_node(c, node->right);
1175
+ if (node->third) {
1176
+ bc_emit(c->bc, BC_JUMP);
1177
+ int patch2 = c->bc->pos;
1178
+ bc_emit_i32(c->bc, 0);
1179
+ bc_patch(c->bc, patch1);
1180
+ compile_node(c, node->third);
1181
+ bc_patch(c->bc, patch2);
1182
+ } else {
1183
+ bc_patch(c->bc, patch1);
1184
+ }
1185
+ break;
1186
+
1187
+ case NODE_WHILE: {
1188
+ int loop_start = c->bc->pos;
1189
+ // Push loop info for BREAK/CONTINUE
1190
+ loop_stack[loop_sp].break_count = 0;
1191
+ loop_stack[loop_sp].continue_count = 0;
1192
+ loop_sp++;
1193
+ compile_node(c, node->left);
1194
+ bc_emit(c->bc, BC_JUMP_IFNOT);
1195
+ int patch1 = c->bc->pos;
1196
+ bc_emit_i32(c->bc, 0);
1197
+ compile_node(c, node->right);
1198
+ bc_emit(c->bc, BC_JUMP);
1199
+ bc_emit_i32(c->bc, loop_start - (c->bc->pos + 4));
1200
+ bc_patch(c->bc, patch1);
1201
+ // Patch all BREAK jumps to here
1202
+ loop_sp--;
1203
+ for (int bi = 0; bi < loop_stack[loop_sp].break_count; bi++) {
1204
+ int pa = loop_stack[loop_sp].break_patches[bi];
1205
+ int32_t o = c->bc->pos - pa - 4;
1206
+ c->bc->code[pa] = o & 0xFF;
1207
+ c->bc->code[pa+1] = (o >> 8) & 0xFF;
1208
+ c->bc->code[pa+2] = (o >> 16) & 0xFF;
1209
+ c->bc->code[pa+3] = (o >> 24) & 0xFF;
1210
+ }
1211
+ // Patch all CONTINUE jumps to loop start
1212
+ for (int ci = 0; ci < loop_stack[loop_sp].continue_count; ci++) {
1213
+ int pa = loop_stack[loop_sp].continue_patches[ci];
1214
+ int32_t o = loop_start - pa - 4;
1215
+ c->bc->code[pa] = o & 0xFF;
1216
+ c->bc->code[pa+1] = (o >> 8) & 0xFF;
1217
+ c->bc->code[pa+2] = (o >> 16) & 0xFF;
1218
+ c->bc->code[pa+3] = (o >> 24) & 0xFF;
1219
+ }
1220
+ } break;
1221
+
1222
+ case NODE_FOR: {
1223
+ // Init
1224
+ ASTNode* range = node->left;
1225
+ ASTNode* start = range->left;
1226
+ ASTNode* end = range->right;
1227
+ ASTNode* step = NULL;
1228
+ // Check for STEP node
1229
+ if (range->type == NODE_BINARY && strcmp(range->op, "STEP") == 0) {
1230
+ start = range->left->left;
1231
+ end = range->left->right;
1232
+ step = range->right;
1233
+ }
1234
+ compile_node(c, start);
1235
+ bc_emit(c->bc, BC_STORE);
1236
+ bc_emit_i32(c->bc, st_intern(c->strings, node->str_val));
1237
+
1238
+ // Condition
1239
+ int loop_start = c->bc->pos;
1240
+ bc_emit(c->bc, BC_LOAD);
1241
+ bc_emit_i32(c->bc, st_intern(c->strings, node->str_val));
1242
+ compile_node(c, end);
1243
+ bc_emit(c->bc, BC_LTE);
1244
+ bc_emit(c->bc, BC_JUMP_IFNOT);
1245
+ int patch1 = c->bc->pos;
1246
+ bc_emit_i32(c->bc, 0);
1247
+
1248
+ // Body
1249
+ loop_stack[loop_sp].break_count = 0;
1250
+ loop_stack[loop_sp].continue_count = 0;
1251
+ loop_sp++;
1252
+ compile_node(c, node->right);
1253
+ // Record increment position for CONTINUE patching
1254
+ int increment_pos = c->bc->pos;
1255
+
1256
+ // Increment
1257
+ bc_emit(c->bc, BC_LOAD);
1258
+ bc_emit_i32(c->bc, st_intern(c->strings, node->str_val));
1259
+ if (step) {
1260
+ compile_node(c, step);
1261
+ } else {
1262
+ bc_emit(c->bc, BC_PUSH_INT);
1263
+ bc_emit_i64(c->bc, 1);
1264
+ }
1265
+ bc_emit(c->bc, BC_ADD);
1266
+ bc_emit(c->bc, BC_STORE);
1267
+ bc_emit_i32(c->bc, st_intern(c->strings, node->str_val));
1268
+
1269
+ bc_emit(c->bc, BC_JUMP);
1270
+ bc_emit_i32(c->bc, loop_start - (c->bc->pos + 4));
1271
+ bc_patch(c->bc, patch1);
1272
+ // Patch all BREAK jumps to here (end of loop)
1273
+ loop_sp--;
1274
+ for (int bi = 0; bi < loop_stack[loop_sp].break_count; bi++) {
1275
+ int pa = loop_stack[loop_sp].break_patches[bi];
1276
+ int32_t o = c->bc->pos - pa - 4;
1277
+ c->bc->code[pa] = o & 0xFF;
1278
+ c->bc->code[pa+1] = (o >> 8) & 0xFF;
1279
+ c->bc->code[pa+2] = (o >> 16) & 0xFF;
1280
+ c->bc->code[pa+3] = (o >> 24) & 0xFF;
1281
+ }
1282
+ // Patch all CONTINUE jumps to increment step
1283
+ for (int ci = 0; ci < loop_stack[loop_sp].continue_count; ci++) {
1284
+ int pa = loop_stack[loop_sp].continue_patches[ci];
1285
+ int32_t o = increment_pos - pa - 4;
1286
+ c->bc->code[pa] = o & 0xFF;
1287
+ c->bc->code[pa+1] = (o >> 8) & 0xFF;
1288
+ c->bc->code[pa+2] = (o >> 16) & 0xFF;
1289
+ c->bc->code[pa+3] = (o >> 24) & 0xFF;
1290
+ }
1291
+ } break;
1292
+
1293
+ case NODE_FUNC_DEF: {
1294
+ // Skip over the JUMP at runtime
1295
+ bc_emit(c->bc, BC_JUMP);
1296
+ int skip_patch = c->bc->pos;
1297
+ bc_emit_i32(c->bc, 0);
1298
+ // Function body starts here — record address AFTER the jump
1299
+ int func_idx = ft_add(c->funcs, node->str_val, c->bc->pos, node->params, node->param_count);
1300
+ compile_node(c, node->right);
1301
+ bc_emit(c->bc, BC_PUSH_NULL);
1302
+ bc_emit(c->bc, BC_RET);
1303
+ bc_patch(c->bc, skip_patch);
1304
+ (void)func_idx;
1305
+ } break;
1306
+
1307
+ case NODE_FUNC_CALL: {
1308
+ // Special-case PUSH(arr, val) and POP(arr) for in-place mutation
1309
+ if (strcmp(node->str_val, "PUSH") == 0 && node->child_count == 2 &&
1310
+ node->children[0]->type == NODE_IDENT) {
1311
+ // PUSH(arr, val): load arr, load val, BC_ARRAY_PUSH, DUP, STORE arr
1312
+ compile_node(c, node->children[0]); // LOAD arr
1313
+ compile_node(c, node->children[1]); // LOAD val
1314
+ bc_emit(c->bc, BC_ARRAY_PUSH);
1315
+ bc_emit(c->bc, BC_DUP);
1316
+ bc_emit(c->bc, BC_STORE);
1317
+ bc_emit_i32(c->bc, st_intern(c->strings, node->children[0]->str_val));
1318
+ break;
1319
+ }
1320
+ if (strcmp(node->str_val, "POP") == 0 && node->child_count == 1 &&
1321
+ node->children[0]->type == NODE_IDENT) {
1322
+ // POP(arr): BC_ARRAY_POP pushes [modified_arr, popped_val]
1323
+ // SWAP → [popped_val, modified_arr] → STORE arr → [popped_val]
1324
+ compile_node(c, node->children[0]); // LOAD arr
1325
+ bc_emit(c->bc, BC_ARRAY_POP); // pushes modified_arr, then popped_val
1326
+ bc_emit(c->bc, BC_SWAP); // → [popped_val, modified_arr]
1327
+ bc_emit(c->bc, BC_STORE); // stores modified_arr → arr
1328
+ bc_emit_i32(c->bc, st_intern(c->strings, node->children[0]->str_val));
1329
+ // stack now has [popped_val]
1330
+ break;
1331
+ }
1332
+ // Push args in reverse
1333
+ for (int i = node->child_count - 1; i >= 0; i--)
1334
+ compile_node(c, node->children[i]);
1335
+ bc_emit(c->bc, BC_CALL);
1336
+ bc_emit_i32(c->bc, st_intern(c->strings, node->str_val));
1337
+ bc_emit_i32(c->bc, node->child_count);
1338
+ } break;
1339
+
1340
+ case NODE_RETURN:
1341
+ if (node->left) compile_node(c, node->left);
1342
+ else { bc_emit(c->bc, BC_PUSH_NULL); }
1343
+ bc_emit(c->bc, BC_RET);
1344
+ break;
1345
+
1346
+ case NODE_BREAK:
1347
+ bc_emit(c->bc, BC_JUMP);
1348
+ if (loop_sp > 0) {
1349
+ LoopInfo* li = &loop_stack[loop_sp - 1];
1350
+ if (li->break_count < 64)
1351
+ li->break_patches[li->break_count++] = c->bc->pos;
1352
+ }
1353
+ bc_emit_i32(c->bc, 0); // patched later
1354
+ break;
1355
+
1356
+ case NODE_CONTINUE:
1357
+ bc_emit(c->bc, BC_JUMP);
1358
+ if (loop_sp > 0) {
1359
+ LoopInfo* li = &loop_stack[loop_sp - 1];
1360
+ if (li->continue_count < 64)
1361
+ li->continue_patches[li->continue_count++] = c->bc->pos;
1362
+ }
1363
+ bc_emit_i32(c->bc, 0); // patched later
1364
+ break;
1365
+
1366
+ case NODE_ARRAY_ASSIGN:
1367
+ // node->left is NODE_ARRAY_INDEX (left=array ident, right=index)
1368
+ // node->right is value expression
1369
+ // We need to: load array, load index, load value, BC_ARRAY_SET, store back
1370
+ {
1371
+ ASTNode* arr_idx = node->left;
1372
+ ASTNode* arr_ident = arr_idx->left;
1373
+ if (arr_ident->type == NODE_IDENT) {
1374
+ compile_node(c, arr_ident); // push array (deep copy)
1375
+ compile_node(c, arr_idx->right); // push index
1376
+ compile_node(c, node->right); // push value
1377
+ bc_emit(c->bc, BC_ARRAY_SET);
1378
+ bc_emit(c->bc, BC_STORE);
1379
+ bc_emit_i32(c->bc, st_intern(c->strings, arr_ident->str_val));
1380
+ }
1381
+ }
1382
+ break;
1383
+
1384
+ case NODE_TRY: {
1385
+ bc_emit(c->bc, BC_TRY);
1386
+ int try_patch = c->bc->pos;
1387
+ bc_emit_i32(c->bc, 0);
1388
+ compile_node(c, node->right); // try block
1389
+ bc_emit(c->bc, BC_ENDTRY);
1390
+ bc_emit(c->bc, BC_JUMP);
1391
+ int catch_patch = c->bc->pos;
1392
+ bc_emit_i32(c->bc, 0);
1393
+ bc_patch(c->bc, try_patch);
1394
+ if (node->third) {
1395
+ // Store error into catch variable if specified
1396
+ if (node->str_val[0]) {
1397
+ // Load error from stack (pushed by BC_THROW handler)
1398
+ // Actually, for simplicity: CATCH block just runs, error is in str_val variable
1399
+ bc_emit(c->bc, BC_STORE);
1400
+ bc_emit_i32(c->bc, st_intern(c->strings, node->str_val));
1401
+ }
1402
+ compile_node(c, node->third); // catch block
1403
+ }
1404
+ bc_patch(c->bc, catch_patch);
1405
+ } break;
1406
+
1407
+ case NODE_THROW:
1408
+ compile_node(c, node->left);
1409
+ bc_emit(c->bc, BC_THROW);
1410
+ break;
1411
+
1412
+ case NODE_ARRAY_LIT:
1413
+ for (int i = 0; i < node->child_count; i++)
1414
+ compile_node(c, node->children[i]);
1415
+ bc_emit(c->bc, BC_ARRAY_NEW);
1416
+ bc_emit_i32(c->bc, node->child_count);
1417
+ break;
1418
+
1419
+ case NODE_ARRAY_INDEX:
1420
+ compile_node(c, node->left);
1421
+ compile_node(c, node->right);
1422
+ bc_emit(c->bc, BC_ARRAY_GET);
1423
+ break;
1424
+
1425
+ case NODE_ARRAY_LEN:
1426
+ compile_node(c, node->left);
1427
+ bc_emit(c->bc, BC_ARRAY_LEN);
1428
+ break;
1429
+
1430
+ case NODE_TYPEOF:
1431
+ compile_node(c, node->left);
1432
+ bc_emit(c->bc, BC_TYPEOF);
1433
+ break;
1434
+
1435
+ case NODE_TOSTR:
1436
+ compile_node(c, node->left);
1437
+ bc_emit(c->bc, BC_TOSTR);
1438
+ break;
1439
+
1440
+ case NODE_TONUM:
1441
+ compile_node(c, node->left);
1442
+ bc_emit(c->bc, BC_TONUM);
1443
+ break;
1444
+
1445
+ case NODE_BLOCK:
1446
+ for (int i = 0; i < node->child_count; i++)
1447
+ compile_node(c, node->children[i]);
1448
+ break;
1449
+
1450
+ case NODE_PROGRAM:
1451
+ for (int i = 0; i < node->child_count; i++)
1452
+ compile_node(c, node->children[i]);
1453
+ bc_emit(c->bc, BC_HALT);
1454
+ break;
1455
+
1456
+ default: break;
1457
+ }
1458
+ }
1459
+
1460
+ // ============================================================================
1461
+ // VM EXECUTION
1462
+ // ============================================================================
1463
+
1464
+ typedef struct {
1465
+ Value stack[8192];
1466
+ int sp;
1467
+ Value vars[1024]; // global variables (indexed by string table)
1468
+ uint8_t* code;
1469
+ int ip;
1470
+ int running;
1471
+ StringTable* strings;
1472
+ FuncTable* funcs;
1473
+ int debug;
1474
+
1475
+ // Call stack with variable save/restore for recursion
1476
+ struct {
1477
+ int ret_ip;
1478
+ int saved_var_idx[256];
1479
+ Value saved_vars[256];
1480
+ int saved_count;
1481
+ } call_stack[256];
1482
+ int call_sp;
1483
+
1484
+ // Try/catch stack
1485
+ struct {
1486
+ int handler_ip;
1487
+ int saved_sp;
1488
+ } try_stack[64];
1489
+ int try_sp;
1490
+ } VM;
1491
+
1492
+ void vm_init(VM* vm, uint8_t* code, StringTable* strings, FuncTable* funcs) {
1493
+ memset(vm, 0, sizeof(VM));
1494
+ vm->code = code;
1495
+ vm->strings = strings;
1496
+ vm->funcs = funcs;
1497
+ vm->running = 1;
1498
+ for (int i = 0; i < 1024; i++) vm->vars[i] = val_null();
1499
+ }
1500
+
1501
+ int vm_run(VM* vm) {
1502
+ while (vm->running && vm->ip < 65536) {
1503
+ uint8_t op = vm->code[vm->ip];
1504
+ if (vm->debug) fprintf(stderr, "IP=%d OP=0x%02X SP=%d\n", vm->ip, op, vm->sp);
1505
+ vm->ip++;
1506
+
1507
+ switch (op) {
1508
+ case BC_HALT:
1509
+ vm->running = 0;
1510
+ break;
1511
+
1512
+ case BC_PUSH_INT: {
1513
+ int64_t v;
1514
+ memcpy(&v, vm->code + vm->ip, 8);
1515
+ vm->ip += 8;
1516
+ vm->stack[vm->sp++] = val_int(v);
1517
+ } break;
1518
+
1519
+ case BC_PUSH_STR: {
1520
+ int32_t idx;
1521
+ memcpy(&idx, vm->code + vm->ip, 4);
1522
+ vm->ip += 4;
1523
+ vm->stack[vm->sp++] = val_string(vm->strings->strings[idx]);
1524
+ } break;
1525
+
1526
+ case BC_PUSH_BOOL: {
1527
+ uint8_t b = vm->code[vm->ip++];
1528
+ vm->stack[vm->sp++] = val_bool(b);
1529
+ } break;
1530
+
1531
+ case BC_PUSH_NULL:
1532
+ vm->stack[vm->sp++] = val_null();
1533
+ break;
1534
+
1535
+ case BC_POP:
1536
+ val_free(vm->stack[--vm->sp]);
1537
+ break;
1538
+
1539
+ case BC_DUP:
1540
+ vm->stack[vm->sp] = vm->stack[vm->sp - 1];
1541
+ vm->sp++;
1542
+ break;
1543
+
1544
+ case BC_SWAP: {
1545
+ Value a = vm->stack[vm->sp - 2];
1546
+ Value b = vm->stack[vm->sp - 1];
1547
+ vm->stack[vm->sp - 2] = b;
1548
+ vm->stack[vm->sp - 1] = a;
1549
+ } break;
1550
+
1551
+ case BC_ADD: {
1552
+ Value b = vm->stack[--vm->sp];
1553
+ Value a = vm->stack[--vm->sp];
1554
+ vm->stack[vm->sp++] = val_add(a, b);
1555
+ val_free(a); val_free(b);
1556
+ } break;
1557
+
1558
+ case BC_SUB: {
1559
+ Value b = vm->stack[--vm->sp];
1560
+ Value a = vm->stack[--vm->sp];
1561
+ vm->stack[vm->sp++] = val_sub(a, b);
1562
+ } break;
1563
+
1564
+ case BC_MUL: {
1565
+ Value b = vm->stack[--vm->sp];
1566
+ Value a = vm->stack[--vm->sp];
1567
+ vm->stack[vm->sp++] = val_mul(a, b);
1568
+ } break;
1569
+
1570
+ case BC_DIV: {
1571
+ Value b = vm->stack[--vm->sp];
1572
+ Value a = vm->stack[--vm->sp];
1573
+ vm->stack[vm->sp++] = val_div(a, b);
1574
+ } break;
1575
+
1576
+ case BC_MOD: {
1577
+ Value b = vm->stack[--vm->sp];
1578
+ Value a = vm->stack[--vm->sp];
1579
+ vm->stack[vm->sp++] = val_mod(a, b);
1580
+ } break;
1581
+
1582
+ case BC_NEG: {
1583
+ Value a = vm->stack[--vm->sp];
1584
+ if (a.type == VAL_INT) vm->stack[vm->sp++] = val_int(-a.as.i);
1585
+ else vm->stack[vm->sp++] = val_null();
1586
+ } break;
1587
+
1588
+ case BC_EQ: {
1589
+ Value b = vm->stack[--vm->sp];
1590
+ Value a = vm->stack[--vm->sp];
1591
+ vm->stack[vm->sp++] = val_bool(val_equals(a, b));
1592
+ } break;
1593
+
1594
+ case BC_NEQ: {
1595
+ Value b = vm->stack[--vm->sp];
1596
+ Value a = vm->stack[--vm->sp];
1597
+ vm->stack[vm->sp++] = val_bool(!val_equals(a, b));
1598
+ } break;
1599
+
1600
+ case BC_LT: {
1601
+ Value b = vm->stack[--vm->sp];
1602
+ Value a = vm->stack[--vm->sp];
1603
+ vm->stack[vm->sp++] = val_bool(val_less(a, b));
1604
+ } break;
1605
+
1606
+ case BC_GT: {
1607
+ Value b = vm->stack[--vm->sp];
1608
+ Value a = vm->stack[--vm->sp];
1609
+ vm->stack[vm->sp++] = val_bool(val_greater(a, b));
1610
+ } break;
1611
+
1612
+ case BC_LTE: {
1613
+ Value b = vm->stack[--vm->sp];
1614
+ Value a = vm->stack[--vm->sp];
1615
+ vm->stack[vm->sp++] = val_bool(val_less(a, b) || val_equals(a, b));
1616
+ } break;
1617
+
1618
+ case BC_GTE: {
1619
+ Value b = vm->stack[--vm->sp];
1620
+ Value a = vm->stack[--vm->sp];
1621
+ vm->stack[vm->sp++] = val_bool(val_greater(a, b) || val_equals(a, b));
1622
+ } break;
1623
+
1624
+ case BC_AND: {
1625
+ Value b = vm->stack[--vm->sp];
1626
+ Value a = vm->stack[--vm->sp];
1627
+ vm->stack[vm->sp++] = val_bool(val_is_truthy(a) && val_is_truthy(b));
1628
+ } break;
1629
+
1630
+ case BC_OR: {
1631
+ Value b = vm->stack[--vm->sp];
1632
+ Value a = vm->stack[--vm->sp];
1633
+ vm->stack[vm->sp++] = val_bool(val_is_truthy(a) || val_is_truthy(b));
1634
+ } break;
1635
+
1636
+ case BC_NOT: {
1637
+ Value a = vm->stack[--vm->sp];
1638
+ vm->stack[vm->sp++] = val_bool(!val_is_truthy(a));
1639
+ } break;
1640
+
1641
+ case BC_BITAND: {
1642
+ Value b = vm->stack[--vm->sp];
1643
+ Value a = vm->stack[--vm->sp];
1644
+ vm->stack[vm->sp++] = val_int(a.as.i & b.as.i);
1645
+ } break;
1646
+
1647
+ case BC_BITOR: {
1648
+ Value b = vm->stack[--vm->sp];
1649
+ Value a = vm->stack[--vm->sp];
1650
+ vm->stack[vm->sp++] = val_int(a.as.i | b.as.i);
1651
+ } break;
1652
+
1653
+ case BC_LSHIFT: {
1654
+ Value b = vm->stack[--vm->sp];
1655
+ Value a = vm->stack[--vm->sp];
1656
+ vm->stack[vm->sp++] = val_int(a.as.i << (int)b.as.i);
1657
+ } break;
1658
+
1659
+ case BC_RSHIFT: {
1660
+ Value b = vm->stack[--vm->sp];
1661
+ Value a = vm->stack[--vm->sp];
1662
+ vm->stack[vm->sp++] = val_int(a.as.i >> (int)b.as.i);
1663
+ } break;
1664
+
1665
+ case BC_STORE: {
1666
+ int32_t idx;
1667
+ memcpy(&idx, vm->code + vm->ip, 4);
1668
+ vm->ip += 4;
1669
+ val_free(vm->vars[idx]);
1670
+ vm->vars[idx] = vm->stack[--vm->sp];
1671
+ } break;
1672
+
1673
+ case BC_LOAD: {
1674
+ int32_t idx;
1675
+ memcpy(&idx, vm->code + vm->ip, 4);
1676
+ vm->ip += 4;
1677
+ Value v = vm->vars[idx];
1678
+ // Deep copy strings and arrays to avoid double-free
1679
+ if (v.type == VAL_STRING) vm->stack[vm->sp++] = val_string(v.as.s);
1680
+ else if (v.type == VAL_ARRAY) {
1681
+ Value arr = val_array();
1682
+ for (int i = 0; i < v.as.a->count; i++) {
1683
+ if (arr.as.a->count >= arr.as.a->capacity) {
1684
+ arr.as.a->capacity = arr.as.a->capacity ? arr.as.a->capacity * 2 : 8;
1685
+ arr.as.a->items = (Value*)realloc(arr.as.a->items, arr.as.a->capacity * sizeof(Value));
1686
+ }
1687
+ // Deep copy each element
1688
+ Value elem = v.as.a->items[i];
1689
+ if (elem.type == VAL_STRING) arr.as.a->items[arr.as.a->count++] = val_string(elem.as.s);
1690
+ else arr.as.a->items[arr.as.a->count++] = elem;
1691
+ }
1692
+ vm->stack[vm->sp++] = arr;
1693
+ } else {
1694
+ vm->stack[vm->sp++] = v;
1695
+ }
1696
+ } break;
1697
+
1698
+ case BC_JUMP: {
1699
+ int32_t offset;
1700
+ memcpy(&offset, vm->code + vm->ip, 4);
1701
+ vm->ip += 4;
1702
+ vm->ip += offset;
1703
+ } break;
1704
+
1705
+ case BC_JUMP_IF: {
1706
+ int32_t offset;
1707
+ memcpy(&offset, vm->code + vm->ip, 4);
1708
+ vm->ip += 4;
1709
+ Value v = vm->stack[--vm->sp];
1710
+ if (val_is_truthy(v)) vm->ip += offset;
1711
+ } break;
1712
+
1713
+ case BC_JUMP_IFNOT: {
1714
+ int32_t offset;
1715
+ memcpy(&offset, vm->code + vm->ip, 4);
1716
+ vm->ip += 4;
1717
+ Value v = vm->stack[--vm->sp];
1718
+ if (!val_is_truthy(v)) vm->ip += offset;
1719
+ } break;
1720
+
1721
+ case BC_PRINT: {
1722
+ Value v = vm->stack[--vm->sp];
1723
+ Value s = val_to_string(v);
1724
+ printf("%s\n", s.as.s);
1725
+ val_free(s);
1726
+ val_free(v);
1727
+ } break;
1728
+
1729
+ case BC_CALL: {
1730
+ int32_t name_idx;
1731
+ memcpy(&name_idx, vm->code + vm->ip, 4);
1732
+ vm->ip += 4;
1733
+ int32_t arity;
1734
+ memcpy(&arity, vm->code + vm->ip, 4);
1735
+ vm->ip += 4;
1736
+
1737
+ const char* name = vm->strings->strings[name_idx];
1738
+
1739
+ // Built-in functions
1740
+ if (strcmp(name, "LEN") == 0) {
1741
+ Value v = vm->stack[--vm->sp];
1742
+ if (v.type == VAL_STRING) {
1743
+ int len = (int)strlen(v.as.s);
1744
+ val_free(v);
1745
+ vm->stack[vm->sp++] = val_int(len);
1746
+ } else if (v.type == VAL_ARRAY) {
1747
+ int len = v.as.a->count;
1748
+ val_free(v);
1749
+ vm->stack[vm->sp++] = val_int(len);
1750
+ } else {
1751
+ val_free(v);
1752
+ vm->stack[vm->sp++] = val_int(0);
1753
+ }
1754
+ } else if (strcmp(name, "TYPEOF") == 0) {
1755
+ Value v = vm->stack[--vm->sp];
1756
+ vm->stack[vm->sp++] = val_string(val_type_name(v));
1757
+ val_free(v);
1758
+ } else if (strcmp(name, "TO_STRING") == 0) {
1759
+ Value v = vm->stack[--vm->sp];
1760
+ vm->stack[vm->sp++] = val_to_string(v);
1761
+ val_free(v);
1762
+ } else if (strcmp(name, "TO_NUMBER") == 0) {
1763
+ Value v = vm->stack[--vm->sp];
1764
+ if (v.type == VAL_STRING) {
1765
+ int64_t n = atoll(v.as.s);
1766
+ val_free(v);
1767
+ vm->stack[vm->sp++] = val_int(n);
1768
+ } else if (v.type == VAL_INT) {
1769
+ vm->stack[vm->sp++] = v;
1770
+ } else {
1771
+ val_free(v);
1772
+ vm->stack[vm->sp++] = val_int(0);
1773
+ }
1774
+ } else if (strcmp(name, "RANDOM") == 0) {
1775
+ for (int i = 0; i < arity; i++) val_free(vm->stack[--vm->sp]);
1776
+ vm->stack[vm->sp++] = val_int(rand() % 100);
1777
+ } else if (strcmp(name, "TIME") == 0) {
1778
+ for (int i = 0; i < arity; i++) val_free(vm->stack[--vm->sp]);
1779
+ vm->stack[vm->sp++] = val_int((int64_t)time(NULL));
1780
+ } else if (strcmp(name, "TO_UPPER") == 0) {
1781
+ Value v = vm->stack[--vm->sp];
1782
+ if (v.type == VAL_STRING) {
1783
+ int len = strlen(v.as.s);
1784
+ char* buf = (char*)malloc(len + 1);
1785
+ for (int i = 0; i < len; i++) buf[i] = toupper((unsigned char)v.as.s[i]);
1786
+ buf[len] = 0;
1787
+ val_free(v);
1788
+ vm->stack[vm->sp++] = val_string(buf);
1789
+ free(buf);
1790
+ } else { val_free(v); vm->stack[vm->sp++] = val_string(""); }
1791
+ } else if (strcmp(name, "TO_LOWER") == 0) {
1792
+ Value v = vm->stack[--vm->sp];
1793
+ if (v.type == VAL_STRING) {
1794
+ int len = strlen(v.as.s);
1795
+ char* buf = (char*)malloc(len + 1);
1796
+ for (int i = 0; i < len; i++) buf[i] = tolower((unsigned char)v.as.s[i]);
1797
+ buf[len] = 0;
1798
+ val_free(v);
1799
+ vm->stack[vm->sp++] = val_string(buf);
1800
+ free(buf);
1801
+ } else { val_free(v); vm->stack[vm->sp++] = val_string(""); }
1802
+ } else if (strcmp(name, "CHAR_AT") == 0) {
1803
+ Value v = vm->stack[--vm->sp];
1804
+ Value idx_val = vm->stack[--vm->sp];
1805
+ if (v.type == VAL_STRING && idx_val.type == VAL_INT) {
1806
+ int len = strlen(v.as.s);
1807
+ if (idx_val.as.i >= 0 && idx_val.as.i < len) {
1808
+ char ch[2] = { v.as.s[idx_val.as.i], 0 };
1809
+ vm->stack[vm->sp++] = val_string(ch);
1810
+ } else { vm->stack[vm->sp++] = val_null(); }
1811
+ } else { vm->stack[vm->sp++] = val_null(); }
1812
+ val_free(v); val_free(idx_val);
1813
+ } else if (strcmp(name, "SUBSTR") == 0) {
1814
+ Value v = vm->stack[--vm->sp];
1815
+ Value start_val = vm->stack[--vm->sp];
1816
+ Value len_val = vm->stack[--vm->sp];
1817
+ if (v.type == VAL_STRING && start_val.type == VAL_INT && len_val.type == VAL_INT) {
1818
+ int slen = strlen(v.as.s);
1819
+ int start = (int)start_val.as.i;
1820
+ int len = (int)len_val.as.i;
1821
+ if (start < 0) start = 0;
1822
+ if (start >= slen) { vm->stack[vm->sp++] = val_string(""); }
1823
+ else {
1824
+ if (start + len > slen) len = slen - start;
1825
+ char* buf = (char*)malloc(len + 1);
1826
+ memcpy(buf, v.as.s + start, len);
1827
+ buf[len] = 0;
1828
+ vm->stack[vm->sp++] = val_string(buf);
1829
+ free(buf);
1830
+ }
1831
+ } else { vm->stack[vm->sp++] = val_string(""); }
1832
+ val_free(v); val_free(start_val); val_free(len_val);
1833
+ } else if (strcmp(name, "TRIM") == 0) {
1834
+ Value v = vm->stack[--vm->sp];
1835
+ if (v.type == VAL_STRING) {
1836
+ const char* s = v.as.s;
1837
+ while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') s++;
1838
+ int len = strlen(s);
1839
+ while (len > 0 && (s[len-1] == ' ' || s[len-1] == '\t' || s[len-1] == '\n' || s[len-1] == '\r')) len--;
1840
+ char* buf = (char*)malloc(len + 1);
1841
+ memcpy(buf, s, len);
1842
+ buf[len] = 0;
1843
+ val_free(v);
1844
+ vm->stack[vm->sp++] = val_string(buf);
1845
+ free(buf);
1846
+ } else { val_free(v); vm->stack[vm->sp++] = val_string(""); }
1847
+ } else if (strcmp(name, "CHAR") == 0) {
1848
+ Value v = vm->stack[--vm->sp];
1849
+ if (v.type == VAL_INT) {
1850
+ char ch[2] = { (char)v.as.i, 0 };
1851
+ vm->stack[vm->sp++] = val_string(ch);
1852
+ } else { vm->stack[vm->sp++] = val_string(""); val_free(v); }
1853
+ } else if (strcmp(name, "ORD") == 0) {
1854
+ Value v = vm->stack[--vm->sp];
1855
+ if (v.type == VAL_STRING && strlen(v.as.s) > 0) {
1856
+ vm->stack[vm->sp++] = val_int((unsigned char)v.as.s[0]);
1857
+ } else { vm->stack[vm->sp++] = val_int(0); val_free(v); }
1858
+ } else if (strcmp(name, "ABS") == 0) {
1859
+ Value v = vm->stack[--vm->sp];
1860
+ if (v.type == VAL_INT) vm->stack[vm->sp++] = val_int(v.as.i < 0 ? -v.as.i : v.as.i);
1861
+ else { vm->stack[vm->sp++] = val_int(0); val_free(v); }
1862
+ } else if (strcmp(name, "MIN") == 0) {
1863
+ Value b = vm->stack[--vm->sp]; Value a = vm->stack[--vm->sp];
1864
+ if (a.type == VAL_INT && b.type == VAL_INT)
1865
+ vm->stack[vm->sp++] = val_int(a.as.i < b.as.i ? a.as.i : b.as.i);
1866
+ else vm->stack[vm->sp++] = val_int(0);
1867
+ val_free(a); val_free(b);
1868
+ } else if (strcmp(name, "MAX") == 0) {
1869
+ Value b = vm->stack[--vm->sp]; Value a = vm->stack[--vm->sp];
1870
+ if (a.type == VAL_INT && b.type == VAL_INT)
1871
+ vm->stack[vm->sp++] = val_int(a.as.i > b.as.i ? a.as.i : b.as.i);
1872
+ else vm->stack[vm->sp++] = val_int(0);
1873
+ val_free(a); val_free(b);
1874
+ } else if (strcmp(name, "SQRT") == 0) {
1875
+ Value v = vm->stack[--vm->sp];
1876
+ if (v.type == VAL_INT) vm->stack[vm->sp++] = val_int((int64_t)sqrt((double)v.as.i));
1877
+ else { vm->stack[vm->sp++] = val_int(0); val_free(v); }
1878
+ } else if (strcmp(name, "POW") == 0) {
1879
+ Value b = vm->stack[--vm->sp]; Value a = vm->stack[--vm->sp];
1880
+ if (a.type == VAL_INT && b.type == VAL_INT)
1881
+ vm->stack[vm->sp++] = val_int((int64_t)pow((double)b.as.i, (double)a.as.i));
1882
+ else vm->stack[vm->sp++] = val_int(0);
1883
+ val_free(a); val_free(b);
1884
+ } else if (strcmp(name, "FLOOR") == 0) {
1885
+ Value v = vm->stack[--vm->sp];
1886
+ vm->stack[vm->sp++] = v;
1887
+ } else if (strcmp(name, "CEIL") == 0) {
1888
+ Value v = vm->stack[--vm->sp];
1889
+ vm->stack[vm->sp++] = v;
1890
+ } else if (strcmp(name, "ROUND") == 0) {
1891
+ Value v = vm->stack[--vm->sp];
1892
+ vm->stack[vm->sp++] = v;
1893
+ } else if (strcmp(name, "PUSH") == 0) {
1894
+ Value val = vm->stack[--vm->sp];
1895
+ Value arr = vm->stack[--vm->sp];
1896
+ if (arr.type == VAL_ARRAY) {
1897
+ if (arr.as.a->count >= arr.as.a->capacity) {
1898
+ int newcap = arr.as.a->capacity ? arr.as.a->capacity * 2 : 8;
1899
+ arr.as.a->items = (Value*)realloc(arr.as.a->items, newcap * sizeof(Value));
1900
+ arr.as.a->capacity = newcap;
1901
+ }
1902
+ arr.as.a->items[arr.as.a->count++] = val;
1903
+ } else val_free(val);
1904
+ vm->stack[vm->sp++] = arr;
1905
+ } else if (strcmp(name, "POP") == 0) {
1906
+ Value arr = vm->stack[--vm->sp];
1907
+ if (arr.type == VAL_ARRAY && arr.as.a->count > 0) {
1908
+ Value v = arr.as.a->items[--arr.as.a->count];
1909
+ vm->stack[vm->sp++] = v;
1910
+ } else vm->stack[vm->sp++] = val_null();
1911
+ val_free(arr);
1912
+ } else if (strcmp(name, "READ_FILE") == 0) {
1913
+ Value v = vm->stack[--vm->sp];
1914
+ if (v.type == VAL_STRING) {
1915
+ FILE* fp = fopen(v.as.s, "rb");
1916
+ if (fp) {
1917
+ fseek(fp, 0, SEEK_END);
1918
+ long sz = ftell(fp);
1919
+ fseek(fp, 0, SEEK_SET);
1920
+ char* buf = (char*)malloc(sz + 1);
1921
+ fread(buf, 1, sz, fp);
1922
+ buf[sz] = 0;
1923
+ fclose(fp);
1924
+ vm->stack[vm->sp++] = val_string(buf);
1925
+ free(buf);
1926
+ } else vm->stack[vm->sp++] = val_null();
1927
+ } else { vm->stack[vm->sp++] = val_null(); val_free(v); }
1928
+ } else if (strcmp(name, "WRITE_FILE") == 0) {
1929
+ Value v = vm->stack[--vm->sp];
1930
+ Value content = vm->stack[--vm->sp];
1931
+ if (v.type == VAL_STRING && content.type == VAL_STRING) {
1932
+ FILE* fp = fopen(v.as.s, "wb");
1933
+ if (fp) {
1934
+ fwrite(content.as.s, 1, strlen(content.as.s), fp);
1935
+ fclose(fp);
1936
+ vm->stack[vm->sp++] = val_bool(1);
1937
+ } else vm->stack[vm->sp++] = val_bool(0);
1938
+ } else vm->stack[vm->sp++] = val_bool(0);
1939
+ val_free(v); val_free(content);
1940
+ } else {
1941
+ // User-defined function
1942
+ int fi = ft_find(vm->funcs, name);
1943
+ if (fi < 0) {
1944
+ fprintf(stderr, "Runtime error: undefined function '%s'\n", name);
1945
+ return 1;
1946
+ }
1947
+ if (vm->debug) fprintf(stderr, "CALL '%s' -> addr=%d\n", name, vm->funcs->entries[fi].addr);
1948
+ if (vm->call_sp >= 256) {
1949
+ fprintf(stderr, "Runtime error: call stack overflow\n");
1950
+ return 1;
1951
+ }
1952
+ // Save variables that will be overwritten by params and locals
1953
+ vm->call_stack[vm->call_sp].ret_ip = vm->ip;
1954
+ vm->call_stack[vm->call_sp].saved_count = 0;
1955
+ int sc = 0;
1956
+ int saved_flag[256] = {0};
1957
+ // Save param slots
1958
+ int pcount = vm->funcs->entries[fi].param_count < arity ? vm->funcs->entries[fi].param_count : arity;
1959
+ for (int i = 0; i < pcount && sc < 256; i++) {
1960
+ int param_idx = st_intern(vm->strings, vm->funcs->entries[fi].params[i]);
1961
+ if (param_idx >= 0 && param_idx < 1024 && !saved_flag[param_idx]) {
1962
+ vm->call_stack[vm->call_sp].saved_var_idx[sc] = param_idx;
1963
+ vm->call_stack[vm->call_sp].saved_vars[sc] = vm->vars[param_idx];
1964
+ vm->call_stack[vm->call_sp].saved_count = ++sc;
1965
+ saved_flag[param_idx] = 1;
1966
+ }
1967
+ }
1968
+ // Store arguments (in reverse so params[0] gets leftmost arg)
1969
+ for (int i = pcount - 1; i >= 0; i--) {
1970
+ int param_idx = st_intern(vm->strings, vm->funcs->entries[fi].params[i]);
1971
+ val_free(vm->vars[param_idx]);
1972
+ vm->vars[param_idx] = vm->stack[--vm->sp];
1973
+ }
1974
+ // Discard remaining args
1975
+ for (int i = arity; i > vm->funcs->entries[fi].param_count; i--)
1976
+ val_free(vm->stack[--vm->sp]);
1977
+ vm->call_sp++;
1978
+ vm->ip = vm->funcs->entries[fi].addr;
1979
+ }
1980
+ } break;
1981
+
1982
+ case BC_RET: {
1983
+ Value v = vm->stack[--vm->sp];
1984
+ if (vm->debug) fprintf(stderr, "RET call_sp=%d\n", vm->call_sp);
1985
+ if (vm->call_sp > 0) {
1986
+ vm->call_sp--;
1987
+ // Restore saved variables
1988
+ for (int i = 0; i < vm->call_stack[vm->call_sp].saved_count; i++) {
1989
+ int gidx = vm->call_stack[vm->call_sp].saved_var_idx[i];
1990
+ if (gidx >= 0 && gidx < 1024) {
1991
+ val_free(vm->vars[gidx]);
1992
+ vm->vars[gidx] = vm->call_stack[vm->call_sp].saved_vars[i];
1993
+ }
1994
+ }
1995
+ vm->ip = vm->call_stack[vm->call_sp].ret_ip;
1996
+ } else {
1997
+ vm->running = 0;
1998
+ }
1999
+ vm->stack[vm->sp++] = v;
2000
+ } break;
2001
+
2002
+ case BC_ARRAY_NEW: {
2003
+ int32_t count;
2004
+ memcpy(&count, vm->code + vm->ip, 4);
2005
+ vm->ip += 4;
2006
+ Value arr = val_array();
2007
+ arr.as.a->capacity = count > 0 ? count : 8;
2008
+ arr.as.a->items = (Value*)malloc(arr.as.a->capacity * sizeof(Value));
2009
+ arr.as.a->count = 0;
2010
+ for (int i = count - 1; i >= 0; i--) {
2011
+ arr.as.a->items[arr.as.a->count++] = vm->stack[--vm->sp];
2012
+ }
2013
+ // Reverse to correct order
2014
+ for (int i = 0; i < arr.as.a->count / 2; i++) {
2015
+ Value tmp = arr.as.a->items[i];
2016
+ arr.as.a->items[i] = arr.as.a->items[arr.as.a->count - 1 - i];
2017
+ arr.as.a->items[arr.as.a->count - 1 - i] = tmp;
2018
+ }
2019
+ vm->stack[vm->sp++] = arr;
2020
+ } break;
2021
+
2022
+ case BC_ARRAY_GET: {
2023
+ Value idx = vm->stack[--vm->sp];
2024
+ Value arr = vm->stack[--vm->sp];
2025
+ if (arr.type == VAL_ARRAY && idx.type == VAL_INT) {
2026
+ if (idx.as.i >= 0 && idx.as.i < arr.as.a->count)
2027
+ vm->stack[vm->sp++] = arr.as.a->items[idx.as.i];
2028
+ else
2029
+ vm->stack[vm->sp++] = val_null();
2030
+ } else if (arr.type == VAL_STRING && idx.type == VAL_INT) {
2031
+ int len = (int)strlen(arr.as.s);
2032
+ if (idx.as.i >= 0 && idx.as.i < len) {
2033
+ char ch[2] = { arr.as.s[idx.as.i], 0 };
2034
+ vm->stack[vm->sp++] = val_string(ch);
2035
+ } else {
2036
+ vm->stack[vm->sp++] = val_null();
2037
+ }
2038
+ } else {
2039
+ vm->stack[vm->sp++] = val_null();
2040
+ }
2041
+ } break;
2042
+
2043
+ case BC_ARRAY_SET: {
2044
+ Value val = vm->stack[--vm->sp];
2045
+ Value idx = vm->stack[--vm->sp];
2046
+ Value arr = vm->stack[--vm->sp];
2047
+ if (arr.type == VAL_ARRAY && idx.type == VAL_INT) {
2048
+ if (idx.as.i >= 0 && idx.as.i < arr.as.a->count) {
2049
+ val_free(arr.as.a->items[idx.as.i]);
2050
+ arr.as.a->items[idx.as.i] = val;
2051
+ }
2052
+ }
2053
+ vm->stack[vm->sp++] = arr;
2054
+ } break;
2055
+
2056
+ case BC_ARRAY_LEN: {
2057
+ Value arr = vm->stack[--vm->sp];
2058
+ if (arr.type == VAL_ARRAY)
2059
+ vm->stack[vm->sp++] = val_int(arr.as.a->count);
2060
+ else if (arr.type == VAL_STRING)
2061
+ vm->stack[vm->sp++] = val_int((int)strlen(arr.as.s));
2062
+ else
2063
+ vm->stack[vm->sp++] = val_int(0);
2064
+ } break;
2065
+
2066
+ case BC_TYPEOF: {
2067
+ Value v = vm->stack[--vm->sp];
2068
+ vm->stack[vm->sp++] = val_string(val_type_name(v));
2069
+ val_free(v);
2070
+ } break;
2071
+
2072
+ case BC_TOSTR: {
2073
+ Value v = vm->stack[--vm->sp];
2074
+ vm->stack[vm->sp++] = val_to_string(v);
2075
+ val_free(v);
2076
+ } break;
2077
+
2078
+ case BC_TONUM: {
2079
+ Value v = vm->stack[--vm->sp];
2080
+ if (v.type == VAL_STRING) {
2081
+ int64_t n = atoll(v.as.s);
2082
+ val_free(v);
2083
+ vm->stack[vm->sp++] = val_int(n);
2084
+ } else if (v.type == VAL_INT) {
2085
+ vm->stack[vm->sp++] = v;
2086
+ } else {
2087
+ val_free(v);
2088
+ vm->stack[vm->sp++] = val_int(0);
2089
+ }
2090
+ } break;
2091
+
2092
+ case BC_ARRAY_PUSH: {
2093
+ Value val = vm->stack[--vm->sp];
2094
+ Value arr = vm->stack[--vm->sp];
2095
+ if (arr.type == VAL_ARRAY) {
2096
+ if (arr.as.a->count >= arr.as.a->capacity) {
2097
+ int newcap = arr.as.a->capacity ? arr.as.a->capacity * 2 : 8;
2098
+ arr.as.a->items = (Value*)realloc(arr.as.a->items, newcap * sizeof(Value));
2099
+ arr.as.a->capacity = newcap;
2100
+ }
2101
+ arr.as.a->items[arr.as.a->count++] = val;
2102
+ } else {
2103
+ val_free(val);
2104
+ }
2105
+ vm->stack[vm->sp++] = arr;
2106
+ } break;
2107
+
2108
+ case BC_ARRAY_POP: {
2109
+ Value arr = vm->stack[--vm->sp];
2110
+ Value popped = val_null();
2111
+ if (arr.type == VAL_ARRAY && arr.as.a->count > 0) {
2112
+ popped = arr.as.a->items[--arr.as.a->count];
2113
+ }
2114
+ vm->stack[vm->sp++] = arr; // push modified array (element removed)
2115
+ vm->stack[vm->sp++] = popped; // push popped value
2116
+ } break;
2117
+
2118
+ case BC_TRY: {
2119
+ int32_t handler_offset;
2120
+ memcpy(&handler_offset, vm->code + vm->ip, 4);
2121
+ vm->ip += 4;
2122
+ if (vm->try_sp < 64) {
2123
+ vm->try_stack[vm->try_sp].handler_ip = vm->ip + handler_offset;
2124
+ vm->try_stack[vm->try_sp].saved_sp = vm->sp;
2125
+ vm->try_sp++;
2126
+ }
2127
+ } break;
2128
+
2129
+ case BC_CATCH: break;
2130
+
2131
+ case BC_THROW: {
2132
+ Value err = vm->stack[--vm->sp];
2133
+ if (vm->try_sp > 0) {
2134
+ vm->try_sp--;
2135
+ vm->sp = vm->try_stack[vm->try_sp].saved_sp;
2136
+ vm->ip = vm->try_stack[vm->try_sp].handler_ip;
2137
+ vm->stack[vm->sp++] = err;
2138
+ } else {
2139
+ fprintf(stderr, "Uncaught exception: %s\n", err.type == VAL_STRING ? err.as.s : "unknown");
2140
+ val_free(err);
2141
+ return 1;
2142
+ }
2143
+ } break;
2144
+
2145
+ case BC_ENDTRY: break;
2146
+
2147
+ default:
2148
+ fprintf(stderr, "Runtime error: unknown opcode 0x%02X at IP=%d\n", op, vm->ip - 1);
2149
+ return 1;
2150
+ }
2151
+ }
2152
+ return 0;
2153
+ }
2154
+
2155
+ // ============================================================================
2156
+ // MAIN
2157
+ // ============================================================================
2158
+
2159
+ int main(int argc, char** argv) {
2160
+ srand((unsigned)time(NULL));
2161
+
2162
+ if (argc < 2) {
2163
+ fprintf(stderr, "Nebulara Interpreter v2.0\n");
2164
+ fprintf(stderr, "Usage: %s <file.nbs>\n", argv[0]);
2165
+ return 1;
2166
+ }
2167
+
2168
+ FILE* f = fopen(argv[1], "rb");
2169
+ if (!f) { fprintf(stderr, "Error: cannot open '%s'\n", argv[1]); return 1; }
2170
+ fseek(f, 0, SEEK_END);
2171
+ long len = ftell(f);
2172
+ fseek(f, 0, SEEK_SET);
2173
+ char* src = (char*)malloc(len + 1);
2174
+ fread(src, 1, len, f);
2175
+ src[len] = 0;
2176
+ fclose(f);
2177
+
2178
+ // Lex
2179
+ Lexer lexer = lexer_new(src);
2180
+ Parser parser = {0};
2181
+ parser.pos = 0;
2182
+ while (1) {
2183
+ NbsToken tok = lexer_next(&lexer);
2184
+ parser.tokens[parser.count++] = tok;
2185
+ if (tok.type == TOK_EOF) break;
2186
+ if (parser.count >= 4096) {
2187
+ fprintf(stderr, "Error: too many tokens\n");
2188
+ free(src);
2189
+ return 1;
2190
+ }
2191
+ }
2192
+ free(src);
2193
+
2194
+ // Parse
2195
+ ASTNode* ast = parse_program(&parser);
2196
+ if (parser.has_error) {
2197
+ fprintf(stderr, "Parse error: %s\n", parser.error_msg);
2198
+ return 1;
2199
+ }
2200
+
2201
+ // Compile
2202
+ BytecodeBuf bc;
2203
+ bc_init(&bc);
2204
+ StringTable strings;
2205
+ st_init(&strings);
2206
+ FuncTable funcs;
2207
+ ft_init(&funcs);
2208
+
2209
+ Compiler compiler = {&bc, &strings, &funcs};
2210
+ compile_node(&compiler, ast);
2211
+
2212
+ // Execute
2213
+ VM* vm = (VM*)calloc(1, sizeof(VM));
2214
+ vm_init(vm, bc.code, &strings, &funcs);
2215
+ vm->debug = 0;
2216
+ for (int i = 1; i < argc; i++) {
2217
+ if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) vm->debug = 1;
2218
+ }
2219
+ int result = vm_run(vm);
2220
+
2221
+ // Cleanup
2222
+ free(vm);
2223
+ free(bc.code);
2224
+ for (int i = 0; i < strings.count; i++) free(strings.strings[i]);
2225
+ free(strings.strings);
2226
+ free(funcs.entries);
2227
+
2228
+ return result;
2229
+ }