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.
- package/Compiler/nbs-bootstrap.c +2229 -0
- package/Compiler/nbs_cli.c +1553 -0
- package/Compiler/neb-cli.exe +0 -0
- package/Compiler/neb-codegen.c +287 -0
- package/Compiler/neb-ffi.c +303 -0
- package/Compiler/neb-ir.c +307 -0
- package/Compiler/neb-knowledge.c +203 -0
- package/Compiler/neb-pipeline.c +787 -0
- package/Compiler/neb-semantic.c +261 -0
- package/Compiler/nebulara.exe +0 -0
- package/README.md +258 -0
- package/SPEC.md +267 -0
- package/bin/neb.js +36 -0
- package/build/neb-cli.exe +0 -0
- package/build/neb-codegen.exe +0 -0
- package/build/neb-ffi.exe +0 -0
- package/build/neb-knowledge.exe +0 -0
- package/build/neb-pipeline.exe +0 -0
- package/build/nebulara.exe +0 -0
- package/dist/index.js +131 -0
- package/package.json +39 -0
- package/std/collections.nbs +72 -0
- package/std/json.nbs +15 -0
- package/std/kanban.nbs +122 -0
- package/std/math.nbs +52 -0
- package/std/net.nbs +22 -0
- package/std/primitives.nbs +30 -0
- package/std/string.nbs +71 -0
- package/std/time.nbs +15 -0
- package/test/broken.nbs +2 -0
- package/test/hello.nbs +6 -0
- package/test/test-arrays.nbs +22 -0
- package/test/test-charat.nbs +7 -0
- package/test/test-cont-debug.nbs +8 -0
- package/test/test-cont-min.nbs +7 -0
- package/test/test-cont-parens.nbs +7 -0
- package/test/test-cont2.nbs +7 -0
- package/test/test-cont3.nbs +7 -0
- package/test/test-control.nbs +18 -0
- package/test/test-functions.nbs +13 -0
- package/test/test-loops.nbs +17 -0
- package/test/test-mod-for.nbs +6 -0
- package/test/test-mod.nbs +5 -0
- package/test/test-new-features.nbs +39 -0
- package/test/test-recursion.nbs +23 -0
- package/test/test-runtime.nbs +8 -0
- package/test/test-strings.nbs +14 -0
- 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
|
+
}
|