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,1553 @@
|
|
|
1
|
+
// Nebulara CLI - Command Line Interface
|
|
2
|
+
// Compiler/nbs_cli.c - The 'nebulara' command
|
|
3
|
+
// Usage: nebulara run <file.nbs> | nebulara build <file.nbs> | nebulara repl
|
|
4
|
+
|
|
5
|
+
#include <stdio.h>
|
|
6
|
+
#include <stdlib.h>
|
|
7
|
+
#include <string.h>
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#include <ctype.h>
|
|
10
|
+
#include <time.h>
|
|
11
|
+
#include <math.h>
|
|
12
|
+
#include <inttypes.h>
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// VM Types (shared with interpreter)
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
#define VAL_NULL 0
|
|
19
|
+
#define VAL_INT 1
|
|
20
|
+
#define VAL_STRING 2
|
|
21
|
+
#define VAL_BOOL 3
|
|
22
|
+
#define VAL_ARRAY 4
|
|
23
|
+
#define VAL_ERROR 5
|
|
24
|
+
|
|
25
|
+
typedef struct Value Value;
|
|
26
|
+
typedef struct { Value* items; int count, cap; } ValueArray;
|
|
27
|
+
struct Value {
|
|
28
|
+
int type;
|
|
29
|
+
union { int64_t i; char* s; int b; ValueArray* a; } as;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
Value val_int_v(int64_t v) { return (Value){VAL_INT, {.i=v}}; }
|
|
33
|
+
Value val_null_v(void) { return (Value){VAL_NULL, {0}}; }
|
|
34
|
+
Value val_string_v(const char* s) { Value v={VAL_STRING}; v.as.s=strdup(s); return v; }
|
|
35
|
+
Value val_bool_v(int b) { return (Value){VAL_BOOL, {.b=b}}; }
|
|
36
|
+
|
|
37
|
+
const char* val_type_name(Value v) {
|
|
38
|
+
switch(v.type){
|
|
39
|
+
case VAL_NULL: return "null";
|
|
40
|
+
case VAL_INT: return "int";
|
|
41
|
+
case VAL_STRING: return "string";
|
|
42
|
+
case VAL_BOOL: return "bool";
|
|
43
|
+
case VAL_ARRAY: return "array";
|
|
44
|
+
case VAL_ERROR: return "error";
|
|
45
|
+
default: return "unknown";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Deep copy a value (strings are duplicated, arrays are deep-copied)
|
|
50
|
+
Value val_copy(Value v) {
|
|
51
|
+
if (v.type == VAL_STRING) return val_string_v(v.as.s);
|
|
52
|
+
if (v.type == VAL_ARRAY) {
|
|
53
|
+
ValueArray* src = v.as.a;
|
|
54
|
+
ValueArray* dst = calloc(1, sizeof(ValueArray));
|
|
55
|
+
dst->cap = src->count > 0 ? src->count : 8;
|
|
56
|
+
dst->items = calloc(dst->cap, sizeof(Value));
|
|
57
|
+
dst->count = src->count;
|
|
58
|
+
for (int i = 0; i < src->count; i++)
|
|
59
|
+
dst->items[i] = val_copy(src->items[i]);
|
|
60
|
+
Value rv = {VAL_ARRAY}; rv.as.a = dst;
|
|
61
|
+
return rv;
|
|
62
|
+
}
|
|
63
|
+
return v; // int, bool, null are copied by value
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Value val_to_string(Value v) {
|
|
67
|
+
char buf[128];
|
|
68
|
+
switch(v.type){
|
|
69
|
+
case VAL_NULL: return val_string_v("null");
|
|
70
|
+
case VAL_INT: snprintf(buf,128,"%" PRId64,v.as.i); return val_string_v(buf);
|
|
71
|
+
case VAL_BOOL: return val_string_v(v.as.b?"true":"false");
|
|
72
|
+
case VAL_STRING: return val_string_v(v.as.s);
|
|
73
|
+
case VAL_ARRAY: return val_string_v("[array]");
|
|
74
|
+
case VAL_ERROR: return val_string_v("[error]");
|
|
75
|
+
default: return val_string_v("unknown");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
int val_truthy(Value v) {
|
|
80
|
+
if (v.type == VAL_NULL) return 0;
|
|
81
|
+
if (v.type == VAL_INT) return v.as.i != 0;
|
|
82
|
+
if (v.type == VAL_BOOL) return v.as.b;
|
|
83
|
+
if (v.type == VAL_STRING) return v.as.s[0] != 0;
|
|
84
|
+
if (v.type == VAL_ARRAY) return v.as.a->count > 0;
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Free a value and all its children
|
|
89
|
+
void val_free(Value v) {
|
|
90
|
+
if (v.type == VAL_STRING) { free(v.as.s); return; }
|
|
91
|
+
if (v.type == VAL_ARRAY) {
|
|
92
|
+
ValueArray* a = v.as.a;
|
|
93
|
+
if (a) {
|
|
94
|
+
for (int i = 0; i < a->count; i++) val_free(a->items[i]);
|
|
95
|
+
free(a->items);
|
|
96
|
+
free(a);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// OPCODES
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
#define OP_PUSH_INT 0x01
|
|
106
|
+
#define OP_PUSH_STR 0x02
|
|
107
|
+
#define OP_PUSH_BOOL 0x03
|
|
108
|
+
#define OP_POP 0x04
|
|
109
|
+
#define OP_ADD 0x05
|
|
110
|
+
#define OP_SUB 0x06
|
|
111
|
+
#define OP_MUL 0x07
|
|
112
|
+
#define OP_DIV 0x08
|
|
113
|
+
#define OP_MOD 0x09
|
|
114
|
+
#define OP_NEG 0x0A
|
|
115
|
+
#define OP_EQ 0x0B
|
|
116
|
+
#define OP_NEQ 0x0C
|
|
117
|
+
#define OP_LT 0x0D
|
|
118
|
+
#define OP_GT 0x0E
|
|
119
|
+
#define OP_LTE 0x0F
|
|
120
|
+
#define OP_GTE 0x10
|
|
121
|
+
#define OP_AND 0x11
|
|
122
|
+
#define OP_OR 0x12
|
|
123
|
+
#define OP_NOT 0x13
|
|
124
|
+
#define OP_STORE 0x14
|
|
125
|
+
#define OP_LOAD 0x15
|
|
126
|
+
#define OP_PRINT 0x16
|
|
127
|
+
#define OP_JUMP 0x17
|
|
128
|
+
#define OP_JUMP_IFNOT 0x18
|
|
129
|
+
#define OP_HALT 0x19
|
|
130
|
+
#define OP_CALL 0x1A
|
|
131
|
+
#define OP_RET 0x1B
|
|
132
|
+
#define OP_ARRAY_NEW 0x1C
|
|
133
|
+
#define OP_ARRAY_GET 0x1D
|
|
134
|
+
#define OP_ARRAY_LEN 0x1E
|
|
135
|
+
#define OP_TYPEOF 0x1F
|
|
136
|
+
#define OP_TOSTR 0x20
|
|
137
|
+
#define OP_TONUM 0x21
|
|
138
|
+
#define OP_ABS 0x22
|
|
139
|
+
#define OP_STRING_ADD 0x23
|
|
140
|
+
#define OP_MIN 0x24
|
|
141
|
+
#define OP_MAX 0x25
|
|
142
|
+
#define OP_SQRT 0x26
|
|
143
|
+
#define OP_POW 0x27
|
|
144
|
+
#define OP_FLOOR 0x28
|
|
145
|
+
#define OP_CEIL 0x29
|
|
146
|
+
#define OP_ROUND 0x2A
|
|
147
|
+
#define OP_READ_FILE 0x2B
|
|
148
|
+
#define OP_WRITE_FILE 0x2C
|
|
149
|
+
#define OP_ARRAY_PUSH 0x2D
|
|
150
|
+
#define OP_ARRAY_POP 0x2E
|
|
151
|
+
#define OP_SUBSTR 0x2F
|
|
152
|
+
#define OP_CHAR_AT 0x30
|
|
153
|
+
#define OP_TO_UPPER 0x31
|
|
154
|
+
#define OP_TO_LOWER 0x32
|
|
155
|
+
#define OP_WRITE_BYTES 0x47
|
|
156
|
+
#define OP_STR_EQ 0x33
|
|
157
|
+
#define OP_ARRAY_SET 0x34
|
|
158
|
+
#define OP_PUSH_NULL 0x35
|
|
159
|
+
#define OP_DUP 0x36
|
|
160
|
+
#define OP_SWAP 0x37
|
|
161
|
+
#define OP_EXIT 0x38
|
|
162
|
+
#define OP_BREAK 0x39
|
|
163
|
+
#define OP_CONTINUE 0x3A
|
|
164
|
+
#define OP_BITAND 0x3B
|
|
165
|
+
#define OP_BITOR 0x3C
|
|
166
|
+
#define OP_LSHIFT 0x3D
|
|
167
|
+
#define OP_RSHIFT 0x3E
|
|
168
|
+
#define OP_TRY 0x3F
|
|
169
|
+
#define OP_CATCH 0x40
|
|
170
|
+
#define OP_THROW 0x41
|
|
171
|
+
#define OP_FINALLY 0x42
|
|
172
|
+
#define OP_ENDTRY 0x43
|
|
173
|
+
#define OP_CHAR 0x44
|
|
174
|
+
#define OP_ORD 0x45
|
|
175
|
+
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// LEXER
|
|
178
|
+
// ============================================================================
|
|
179
|
+
|
|
180
|
+
typedef enum {
|
|
181
|
+
T_INT, T_STR, T_IDENT, T_PLUS, T_MINUS, T_STAR, T_SLASH, T_MOD,
|
|
182
|
+
T_EQ, T_NEQ, T_LT, T_GT, T_LTE, T_GTE, T_AND, T_OR, T_NOT,
|
|
183
|
+
T_ASSIGN, T_LPAREN, T_RPAREN, T_LBRACKET, T_RBRACKET,
|
|
184
|
+
T_COMMA, T_COLON, T_PRINT, T_IF, T_ELSE, T_ELSEIF, T_THEN,
|
|
185
|
+
T_WHILE, T_FOR, T_TO, T_STEP, T_FUNC, T_END, T_RETURN, T_LET, T_BREAK, T_CONTINUE,
|
|
186
|
+
T_BITAND, T_BITOR, T_LSHIFT, T_RSHIFT,
|
|
187
|
+
T_TRY, T_CATCH, T_THROW, T_FINALLY,
|
|
188
|
+
T_TRUE, T_FALSE, T_NULL,
|
|
189
|
+
T_EOF
|
|
190
|
+
} TT;
|
|
191
|
+
|
|
192
|
+
typedef struct { TT type; char txt[256]; int64_t ival; int line; } Tk;
|
|
193
|
+
typedef struct { const char*s; int p,l,line; } Lx;
|
|
194
|
+
|
|
195
|
+
static Tk tks[8192]; static int tn=0,tp=0;
|
|
196
|
+
|
|
197
|
+
static Tk lx(Lx*l) {
|
|
198
|
+
while(l->p<l->l) {
|
|
199
|
+
char c=l->s[l->p];
|
|
200
|
+
if(c==' '||c=='\t'||c=='\r') { l->p++; continue; }
|
|
201
|
+
if(c=='\n') { l->p++; l->line++; continue; }
|
|
202
|
+
if(c=='#') { while(l->p<l->l&&l->s[l->p]!='\n')l->p++; continue; }
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
Tk t={0};
|
|
206
|
+
t.line=l->line;
|
|
207
|
+
if(l->p>=l->l){t.type=T_EOF;return t;}
|
|
208
|
+
char c=l->s[l->p];
|
|
209
|
+
|
|
210
|
+
if(c>='0'&&c<='9'){
|
|
211
|
+
int64_t v=0;
|
|
212
|
+
while(l->p<l->l&&l->s[l->p]>='0'&&l->s[l->p]<='9')v=v*10+(l->s[l->p++]-'0');
|
|
213
|
+
t.type=T_INT;t.ival=v;return t;
|
|
214
|
+
}
|
|
215
|
+
if(c=='"'){
|
|
216
|
+
l->p++;int i=0;
|
|
217
|
+
while(l->p<l->l&&l->s[l->p]!='"'&&i<254){
|
|
218
|
+
if(l->s[l->p]=='\\'){
|
|
219
|
+
if(l->p+1<l->l){
|
|
220
|
+
l->p++;char e=l->s[l->p++];
|
|
221
|
+
if(e=='n')t.txt[i++]='\n';else if(e=='t')t.txt[i++]='\t';
|
|
222
|
+
else if(e=='\\')t.txt[i++]='\\';else if(e=='"')t.txt[i++]='"';
|
|
223
|
+
else if(e=='0')t.txt[i++]='\0';else t.txt[i++]=e;
|
|
224
|
+
} else { l->p++; }
|
|
225
|
+
} else t.txt[i++]=l->s[l->p++];
|
|
226
|
+
}
|
|
227
|
+
t.txt[i]=0;if(l->p<l->l)l->p++;t.type=T_STR;return t;
|
|
228
|
+
}
|
|
229
|
+
if((c>='a'&&c<='z')||(c>='A'&&c<='Z')||c=='_'){
|
|
230
|
+
int i=0;
|
|
231
|
+
while(l->p<l->l&&i<254&&((l->s[l->p]>='a'&&l->s[l->p]<='z')||(l->s[l->p]>='A'&&l->s[l->p]<='Z')||(l->s[l->p]>='0'&&l->s[l->p]<='9')||l->s[l->p]=='_'||l->s[l->p]=='!'||l->s[l->p]=='?'))t.txt[i++]=l->s[l->p++];
|
|
232
|
+
t.txt[i]=0;
|
|
233
|
+
if(!strcmp(t.txt,"PRINT"))t.type=T_PRINT;
|
|
234
|
+
else if(!strcmp(t.txt,"IF?")||!strcmp(t.txt,"IF"))t.type=T_IF;
|
|
235
|
+
else if(!strcmp(t.txt,"ELSE"))t.type=T_ELSE;
|
|
236
|
+
else if(!strcmp(t.txt,"ELSEIF")||!strcmp(t.txt,"ELSEIF?"))t.type=T_ELSEIF;
|
|
237
|
+
else if(!strcmp(t.txt,"THEN"))t.type=T_THEN;
|
|
238
|
+
else if(!strcmp(t.txt,"WHILE?")||!strcmp(t.txt,"WHILE"))t.type=T_WHILE;
|
|
239
|
+
else if(!strcmp(t.txt,"FOR!"))t.type=T_FOR;
|
|
240
|
+
else if(!strcmp(t.txt,"TO"))t.type=T_TO;
|
|
241
|
+
else if(!strcmp(t.txt,"STEP"))t.type=T_STEP;
|
|
242
|
+
else if(!strcmp(t.txt,"FUNC!"))t.type=T_FUNC;
|
|
243
|
+
else if(!strcmp(t.txt,"END!"))t.type=T_END;
|
|
244
|
+
else if(!strcmp(t.txt,"RETURN"))t.type=T_RETURN;
|
|
245
|
+
else if(!strcmp(t.txt,"LET"))t.type=T_LET;
|
|
246
|
+
else if(!strcmp(t.txt,"AND"))t.type=T_AND;
|
|
247
|
+
else if(!strcmp(t.txt,"OR"))t.type=T_OR;
|
|
248
|
+
else if(!strcmp(t.txt,"NOT"))t.type=T_NOT;
|
|
249
|
+
else if(!strcmp(t.txt,"BREAK"))t.type=T_BREAK;
|
|
250
|
+
else if(!strcmp(t.txt,"CONTINUE"))t.type=T_CONTINUE;
|
|
251
|
+
else if(!strcmp(t.txt,"TRUE")){t.type=T_TRUE;}
|
|
252
|
+
else if(!strcmp(t.txt,"FALSE")){t.type=T_FALSE;}
|
|
253
|
+
else if(!strcmp(t.txt,"NULL")){t.type=T_NULL;}
|
|
254
|
+
else if(!strcmp(t.txt,"TRY!"))t.type=T_TRY;
|
|
255
|
+
else if(!strcmp(t.txt,"CATCH!"))t.type=T_CATCH;
|
|
256
|
+
else if(!strcmp(t.txt,"THROW"))t.type=T_THROW;
|
|
257
|
+
else if(!strcmp(t.txt,"FINALLY!"))t.type=T_FINALLY;
|
|
258
|
+
else t.type=T_IDENT;
|
|
259
|
+
return t;
|
|
260
|
+
}
|
|
261
|
+
l->p++;t.txt[0]=c;t.txt[1]=0;
|
|
262
|
+
switch(c){
|
|
263
|
+
case '+':t.type=T_PLUS;break;case '-':t.type=T_MINUS;break;
|
|
264
|
+
case '*':t.type=T_STAR;break;case '/':t.type=T_SLASH;break;
|
|
265
|
+
case '%':t.type=T_MOD;break;
|
|
266
|
+
case '=':if(l->p<l->l&&l->s[l->p]=='='){l->p++;t.txt[1]='=';t.type=T_EQ;}else t.type=T_ASSIGN;break;
|
|
267
|
+
case '!':if(l->p<l->l&&l->s[l->p]=='='){l->p++;t.txt[1]='=';t.type=T_NEQ;}else t.type=T_NOT;break;
|
|
268
|
+
case '<':if(l->p<l->l&&l->s[l->p]=='='){l->p++;t.txt[1]='=';t.type=T_LTE;}else if(l->p<l->l&&l->s[l->p]=='<'){l->p++;t.txt[1]='<';t.type=T_LSHIFT;}else t.type=T_LT;break;
|
|
269
|
+
case '>':if(l->p<l->l&&l->s[l->p]=='='){l->p++;t.txt[1]='=';t.type=T_GTE;}else if(l->p<l->l&&l->s[l->p]=='>'){l->p++;t.txt[1]='>';t.type=T_RSHIFT;}else t.type=T_GT;break;
|
|
270
|
+
case '&':t.type=T_BITAND;break;
|
|
271
|
+
case '|':t.type=T_BITOR;break;
|
|
272
|
+
case '(':t.type=T_LPAREN;break;case ')':t.type=T_RPAREN;break;
|
|
273
|
+
case '[':t.type=T_LBRACKET;break;case ']':t.type=T_RBRACKET;break;
|
|
274
|
+
case ',':t.type=T_COMMA;break;case ':':t.type=T_COLON;break;
|
|
275
|
+
}
|
|
276
|
+
return t;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ============================================================================
|
|
280
|
+
// BYTECODE COMPILER
|
|
281
|
+
// ============================================================================
|
|
282
|
+
|
|
283
|
+
static uint8_t bytecode[131072];
|
|
284
|
+
static int bclen=0;
|
|
285
|
+
static char strings[65536][256];
|
|
286
|
+
static int strcount=0;
|
|
287
|
+
|
|
288
|
+
// Loop break/continue patch stack
|
|
289
|
+
typedef struct { int break_patches[64]; int break_count; int continue_patches[64]; int continue_count; int continue_ip; } LoopInfo;
|
|
290
|
+
static LoopInfo loop_stack[64];
|
|
291
|
+
static int loop_sp=0;
|
|
292
|
+
|
|
293
|
+
// Try/catch patch stack
|
|
294
|
+
typedef struct { int end_patches[32]; int end_count; } TryInfo;
|
|
295
|
+
static TryInfo try_stack[32];
|
|
296
|
+
static int try_sp=0;
|
|
297
|
+
|
|
298
|
+
static void bc(uint8_t b){
|
|
299
|
+
if(bclen>=131072){fprintf(stderr,"Compiler error: bytecode overflow\n");exit(1);}
|
|
300
|
+
bytecode[bclen++]=b;
|
|
301
|
+
}
|
|
302
|
+
static void bc64(int64_t v){for(int i=0;i<8;i++)bc((v>>(i*8))&0xFF);}
|
|
303
|
+
static void bc32(int32_t v){for(int i=0;i<4;i++)bc((v>>(i*8))&0xFF);}
|
|
304
|
+
|
|
305
|
+
static char varnames[256][64];
|
|
306
|
+
static int varcount=0;
|
|
307
|
+
|
|
308
|
+
static int var_idx(const char* name){
|
|
309
|
+
for(int i=0;i<varcount;i++)if(!strcmp(varnames[i],name))return i;
|
|
310
|
+
if(varcount>=256){fprintf(stderr,"Compiler error: too many variables\n");exit(1);}
|
|
311
|
+
strncpy(varnames[varcount],name,63);varnames[varcount][63]=0;return varcount++;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
static int str_idx(const char* s){
|
|
315
|
+
for(int i=0;i<strcount;i++)if(!strcmp(strings[i],s))return i;
|
|
316
|
+
if(strcount>=65536){fprintf(stderr,"Compiler error: too many strings\n");exit(1);}
|
|
317
|
+
strncpy(strings[strcount],s,255);strings[strcount][255]=0;return strcount++;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Function table
|
|
321
|
+
typedef struct { char name[64]; int addr; char params[8][64]; int param_count; int entry_varcount; int local_count; } FuncEntry;
|
|
322
|
+
static FuncEntry func_table[256];
|
|
323
|
+
static int func_count=0;
|
|
324
|
+
|
|
325
|
+
static int ft_add(const char* name, int addr) {
|
|
326
|
+
if(func_count>=256){fprintf(stderr,"Compiler error: too many functions\n");exit(1);}
|
|
327
|
+
int idx = func_count++;
|
|
328
|
+
strncpy(func_table[idx].name, name, 63);func_table[idx].name[63]=0;
|
|
329
|
+
func_table[idx].addr = addr;
|
|
330
|
+
func_table[idx].param_count = 0;
|
|
331
|
+
func_table[idx].entry_varcount = 0;
|
|
332
|
+
func_table[idx].local_count = 0;
|
|
333
|
+
return idx;
|
|
334
|
+
}
|
|
335
|
+
static void ft_add_param(int idx, const char* pname) {
|
|
336
|
+
if (idx >= 0 && func_table[idx].param_count < 8) {
|
|
337
|
+
strncpy(func_table[idx].params[func_table[idx].param_count], pname, 63);
|
|
338
|
+
func_table[idx].params[func_table[idx].param_count][63]=0;
|
|
339
|
+
func_table[idx].param_count++;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
static int ft_find(const char* name) {
|
|
343
|
+
for(int i=0;i<func_count;i++) if(!strcmp(func_table[i].name, name)) return i;
|
|
344
|
+
return -1;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
static void compile_expr(void);
|
|
348
|
+
static void compile_stmt(void);
|
|
349
|
+
static void compile_block(void);
|
|
350
|
+
|
|
351
|
+
static void compile_primary(void) {
|
|
352
|
+
if(tp>=tn)return;
|
|
353
|
+
Tk* t=&tks[tp];
|
|
354
|
+
if(t->type==T_INT){tp++;bc(OP_PUSH_INT);bc64(t->ival);return;}
|
|
355
|
+
if(t->type==T_STR){tp++;int si=str_idx(t->txt);bc(OP_PUSH_STR);bc32(si);return;}
|
|
356
|
+
if(t->type==T_TRUE){tp++;bc(OP_PUSH_BOOL);bc(1);return;}
|
|
357
|
+
if(t->type==T_FALSE){tp++;bc(OP_PUSH_BOOL);bc(0);return;}
|
|
358
|
+
if(t->type==T_NULL){tp++;bc(OP_PUSH_NULL);return;}
|
|
359
|
+
if(t->type==T_IDENT){
|
|
360
|
+
if(tp+1<tn && tks[tp+1].type==T_LPAREN) {
|
|
361
|
+
if(!strcmp(t->txt,"TO_STRING")||!strcmp(t->txt,"TYPEOF")||!strcmp(t->txt,"LEN")||
|
|
362
|
+
!strcmp(t->txt,"TO_NUMBER")||
|
|
363
|
+
!strcmp(t->txt,"ABS")||!strcmp(t->txt,"MIN")||!strcmp(t->txt,"MAX")||
|
|
364
|
+
!strcmp(t->txt,"SQRT")||!strcmp(t->txt,"POW")||!strcmp(t->txt,"FLOOR")||
|
|
365
|
+
!strcmp(t->txt,"CEIL")||!strcmp(t->txt,"ROUND")||!strcmp(t->txt,"READ_FILE")||
|
|
366
|
+
!strcmp(t->txt,"WRITE_FILE")||!strcmp(t->txt,"SUBSTR")||!strcmp(t->txt,"CHAR_AT")||
|
|
367
|
+
!strcmp(t->txt,"TO_UPPER")||!strcmp(t->txt,"TO_LOWER")||
|
|
368
|
+
!strcmp(t->txt,"WRITE_BYTES")||
|
|
369
|
+
!strcmp(t->txt,"CHAR")||!strcmp(t->txt,"ORD")) {
|
|
370
|
+
char fname[64]; strncpy(fname,t->txt,63); fname[63]=0; tp+=2;
|
|
371
|
+
int nargs=0;
|
|
372
|
+
if(tp<tn&&tks[tp].type!=T_RPAREN) {
|
|
373
|
+
compile_expr(); nargs++;
|
|
374
|
+
while(tp<tn&&tks[tp].type==T_COMMA) { tp++; compile_expr(); nargs++; }
|
|
375
|
+
}
|
|
376
|
+
if(tp<tn&&tks[tp].type==T_RPAREN)tp++;
|
|
377
|
+
if(!strcmp(fname,"TO_STRING"))bc(OP_TOSTR);
|
|
378
|
+
else if(!strcmp(fname,"TO_NUMBER"))bc(OP_TONUM);
|
|
379
|
+
else if(!strcmp(fname,"TYPEOF"))bc(OP_TYPEOF);
|
|
380
|
+
else if(!strcmp(fname,"LEN"))bc(OP_ARRAY_LEN);
|
|
381
|
+
else if(!strcmp(fname,"ABS"))bc(OP_ABS);
|
|
382
|
+
else if(!strcmp(fname,"MIN"))bc(OP_MIN);
|
|
383
|
+
else if(!strcmp(fname,"MAX"))bc(OP_MAX);
|
|
384
|
+
else if(!strcmp(fname,"SQRT"))bc(OP_SQRT);
|
|
385
|
+
else if(!strcmp(fname,"POW"))bc(OP_POW);
|
|
386
|
+
else if(!strcmp(fname,"FLOOR"))bc(OP_FLOOR);
|
|
387
|
+
else if(!strcmp(fname,"CEIL"))bc(OP_CEIL);
|
|
388
|
+
else if(!strcmp(fname,"ROUND"))bc(OP_ROUND);
|
|
389
|
+
else if(!strcmp(fname,"READ_FILE"))bc(OP_READ_FILE);
|
|
390
|
+
else if(!strcmp(fname,"WRITE_FILE"))bc(OP_WRITE_FILE);
|
|
391
|
+
else if(!strcmp(fname,"SUBSTR"))bc(OP_SUBSTR);
|
|
392
|
+
else if(!strcmp(fname,"CHAR_AT"))bc(OP_CHAR_AT);
|
|
393
|
+
else if(!strcmp(fname,"TO_UPPER"))bc(OP_TO_UPPER);
|
|
394
|
+
else if(!strcmp(fname,"TO_LOWER"))bc(OP_TO_LOWER);
|
|
395
|
+
else if(!strcmp(fname,"WRITE_BYTES"))bc(OP_WRITE_BYTES);
|
|
396
|
+
else if(!strcmp(fname,"CHAR"))bc(OP_CHAR);
|
|
397
|
+
else if(!strcmp(fname,"ORD"))bc(OP_ORD);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
// User-defined or unknown function call
|
|
401
|
+
char fname[64]; strncpy(fname, t->txt, 63); fname[63]=0;
|
|
402
|
+
tp+=2; // skip name and (
|
|
403
|
+
// Push args in reverse order
|
|
404
|
+
int arg_count=0;
|
|
405
|
+
while(tp<tn && tks[tp].type!=T_RPAREN) {
|
|
406
|
+
compile_expr();
|
|
407
|
+
arg_count++;
|
|
408
|
+
if(tp<tn && tks[tp].type==T_COMMA) tp++;
|
|
409
|
+
}
|
|
410
|
+
if(tp<tn) tp++; // skip )
|
|
411
|
+
bc(OP_CALL);
|
|
412
|
+
int si=str_idx(fname);
|
|
413
|
+
bc32(si);
|
|
414
|
+
bc32(arg_count);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
tp++;bc(OP_LOAD);bc32(var_idx(t->txt));
|
|
418
|
+
// Array indexing: arr[i]
|
|
419
|
+
while(tp<tn && tks[tp].type==T_LBRACKET){
|
|
420
|
+
tp++;compile_expr();
|
|
421
|
+
if(tp<tn&&tks[tp].type==T_RBRACKET)tp++;
|
|
422
|
+
bc(OP_ARRAY_GET);
|
|
423
|
+
}
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if(t->type==T_LPAREN){tp++;compile_expr();if(tp<tn&&tks[tp].type==T_RPAREN)tp++;return;}
|
|
427
|
+
if(t->type==T_MINUS){tp++;compile_primary();bc(OP_NEG);return;}
|
|
428
|
+
if(t->type==T_NOT){tp++;compile_primary();bc(OP_NOT);return;}
|
|
429
|
+
if(t->type==T_LBRACKET){
|
|
430
|
+
tp++;int count=0;
|
|
431
|
+
while(tp<tn&&tks[tp].type!=T_RBRACKET){compile_expr();count++;if(tp<tn&&tks[tp].type==T_COMMA)tp++;}
|
|
432
|
+
if(tp<tn)tp++;
|
|
433
|
+
bc(OP_ARRAY_NEW);bc32(count);return;
|
|
434
|
+
}
|
|
435
|
+
// Fallback: push 0
|
|
436
|
+
tp++;bc(OP_PUSH_INT);bc64(0);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Precedence: 1=multiplicative (* / %), 2=additive (+ - << >> & |), 3=comparison (== != < > <= >=)
|
|
440
|
+
static void compile_mul(void) {
|
|
441
|
+
compile_primary();
|
|
442
|
+
while(tp<tn&&(tks[tp].type==T_STAR||tks[tp].type==T_SLASH||tks[tp].type==T_MOD)){
|
|
443
|
+
TT op=tks[tp].type;tp++;
|
|
444
|
+
compile_primary();
|
|
445
|
+
switch(op){
|
|
446
|
+
case T_STAR:bc(OP_MUL);break;case T_SLASH:bc(OP_DIV);break;
|
|
447
|
+
case T_MOD:bc(OP_MOD);break;default:break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
static void compile_add(void) {
|
|
452
|
+
compile_mul();
|
|
453
|
+
while(tp<tn&&(tks[tp].type==T_PLUS||tks[tp].type==T_MINUS||
|
|
454
|
+
tks[tp].type==T_BITAND||tks[tp].type==T_BITOR||
|
|
455
|
+
tks[tp].type==T_LSHIFT||tks[tp].type==T_RSHIFT)){
|
|
456
|
+
TT op=tks[tp].type;tp++;
|
|
457
|
+
compile_mul();
|
|
458
|
+
switch(op){
|
|
459
|
+
case T_PLUS:bc(OP_ADD);break;case T_MINUS:bc(OP_SUB);break;
|
|
460
|
+
case T_BITAND:bc(OP_BITAND);break;case T_BITOR:bc(OP_BITOR);break;
|
|
461
|
+
case T_LSHIFT:bc(OP_LSHIFT);break;case T_RSHIFT:bc(OP_RSHIFT);break;
|
|
462
|
+
default:break;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
static void compile_comparison(void) {
|
|
467
|
+
compile_add();
|
|
468
|
+
while(tp<tn&&(tks[tp].type==T_EQ||tks[tp].type==T_NEQ||
|
|
469
|
+
tks[tp].type==T_LT||tks[tp].type==T_GT||
|
|
470
|
+
tks[tp].type==T_LTE||tks[tp].type==T_GTE)){
|
|
471
|
+
TT op=tks[tp].type;tp++;
|
|
472
|
+
compile_add();
|
|
473
|
+
switch(op){
|
|
474
|
+
case T_EQ:bc(OP_EQ);break;case T_NEQ:bc(OP_NEQ);break;
|
|
475
|
+
case T_LT:bc(OP_LT);break;case T_GT:bc(OP_GT);break;
|
|
476
|
+
case T_LTE:bc(OP_LTE);break;case T_GTE:bc(OP_GTE);break;
|
|
477
|
+
default:break;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
static void compile_expr(void) {
|
|
483
|
+
compile_comparison();
|
|
484
|
+
while(tp<tn&&(tks[tp].type==T_AND||tks[tp].type==T_OR)){
|
|
485
|
+
TT op=tks[tp].type;tp++;
|
|
486
|
+
compile_comparison();
|
|
487
|
+
if(op==T_AND)bc(OP_AND);else bc(OP_OR);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
static void compile_stmt(void) {
|
|
492
|
+
if(tp>=tn)return;
|
|
493
|
+
Tk* t=&tks[tp];
|
|
494
|
+
|
|
495
|
+
if(t->type==T_BREAK){
|
|
496
|
+
tp++;
|
|
497
|
+
if(loop_sp>0){
|
|
498
|
+
bc(OP_JUMP);int patch=bclen;bc32(0);
|
|
499
|
+
LoopInfo* li=&loop_stack[loop_sp-1];
|
|
500
|
+
if(li->break_count<64) li->break_patches[li->break_count++]=patch;
|
|
501
|
+
}
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if(t->type==T_CONTINUE){
|
|
505
|
+
tp++;
|
|
506
|
+
if(loop_sp>0){
|
|
507
|
+
LoopInfo* li=&loop_stack[loop_sp-1];
|
|
508
|
+
bc(OP_JUMP);
|
|
509
|
+
if(li->continue_count<64) li->continue_patches[li->continue_count++]=bclen;
|
|
510
|
+
bc32(0); // patched later
|
|
511
|
+
}
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// TRY! ... CATCH! err: ... FINALLY! ... END!
|
|
516
|
+
if(t->type==T_TRY){
|
|
517
|
+
tp++;
|
|
518
|
+
// Emit OP_TRY which pushes current IP to try stack and sets catch handler
|
|
519
|
+
bc(OP_TRY);int try_patch=bclen;bc32(0);
|
|
520
|
+
// Compile try body
|
|
521
|
+
compile_block();
|
|
522
|
+
// Jump to end (skip catch/finally)
|
|
523
|
+
bc(OP_JUMP);int end_jump=bclen;bc32(0);
|
|
524
|
+
// Patch try to point to catch handler
|
|
525
|
+
{int32_t o=bclen-try_patch-4;memcpy(bytecode+try_patch,&o,4);}
|
|
526
|
+
// CATCH! block
|
|
527
|
+
if(tp<tn&&tks[tp].type==T_CATCH){
|
|
528
|
+
tp++;
|
|
529
|
+
// Store error in variable if named
|
|
530
|
+
if(tp<tn&&tks[tp].type==T_IDENT){
|
|
531
|
+
int idx=var_idx(tks[tp].txt);tp++;
|
|
532
|
+
bc(OP_STORE);bc32(idx);
|
|
533
|
+
}
|
|
534
|
+
compile_block();
|
|
535
|
+
}
|
|
536
|
+
// FINALLY! block (optional)
|
|
537
|
+
if(tp<tn&&tks[tp].type==T_FINALLY){
|
|
538
|
+
tp++;
|
|
539
|
+
compile_block();
|
|
540
|
+
}
|
|
541
|
+
// Patch end jump
|
|
542
|
+
{int32_t o=bclen-end_jump-4;memcpy(bytecode+end_jump,&o,4);}
|
|
543
|
+
if(tp<tn&&tks[tp].type==T_END)tp++;
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
// THROW expression
|
|
547
|
+
if(t->type==T_THROW){
|
|
548
|
+
tp++;
|
|
549
|
+
compile_expr();
|
|
550
|
+
bc(OP_THROW);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if(t->type==T_PRINT){tp++;compile_expr();bc(OP_PRINT);return;}
|
|
555
|
+
|
|
556
|
+
if(t->type==T_LET){
|
|
557
|
+
tp++;
|
|
558
|
+
if(tp<tn&&tks[tp].type==T_IDENT){
|
|
559
|
+
int idx=var_idx(tks[tp].txt);tp++;
|
|
560
|
+
if(tp<tn&&tks[tp].type==T_ASSIGN)tp++;
|
|
561
|
+
compile_expr();
|
|
562
|
+
bc(OP_STORE);bc32(idx);
|
|
563
|
+
}
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if(t->type==T_IDENT&&tp+1<tn&&tks[tp+1].type==T_ASSIGN){
|
|
567
|
+
int idx=var_idx(t->txt);tp+=2;
|
|
568
|
+
compile_expr();
|
|
569
|
+
bc(OP_STORE);bc32(idx);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
// Array indexing assignment: arr[i] = value
|
|
573
|
+
if(t->type==T_IDENT&&tp+2<tn&&tks[tp+1].type==T_LBRACKET){
|
|
574
|
+
int idx=var_idx(t->txt);tp++;
|
|
575
|
+
bc(OP_LOAD);bc32(idx);
|
|
576
|
+
tp++;compile_expr();
|
|
577
|
+
if(tp<tn&&tks[tp].type==T_RBRACKET)tp++;
|
|
578
|
+
if(tp<tn&&tks[tp].type==T_ASSIGN)tp++;
|
|
579
|
+
compile_expr();
|
|
580
|
+
bc(OP_ARRAY_SET);
|
|
581
|
+
bc(OP_STORE);bc32(idx);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
if(t->type==T_IF){
|
|
585
|
+
tp++;
|
|
586
|
+
compile_expr();
|
|
587
|
+
if(tp<tn&&tks[tp].type==T_THEN)tp++;
|
|
588
|
+
if(tp<tn&&tks[tp].type==T_COLON)tp++;
|
|
589
|
+
bc(OP_JUMP_IFNOT);int p1=bclen;bc32(0);
|
|
590
|
+
compile_block();
|
|
591
|
+
// Track JUMP locations that need patching to END
|
|
592
|
+
int end_patches[64]; int end_patch_count=0;
|
|
593
|
+
// Handle ELSE / ELSEIF chains
|
|
594
|
+
while(tp<tn&&(tks[tp].type==T_ELSE||tks[tp].type==T_ELSEIF)){
|
|
595
|
+
int is_elseif=(tks[tp].type==T_ELSEIF);
|
|
596
|
+
tp++;
|
|
597
|
+
if(is_elseif){
|
|
598
|
+
// ELSEIF? <condition>:
|
|
599
|
+
bc(OP_JUMP);end_patches[end_patch_count++]=bclen;bc32(0);
|
|
600
|
+
{int32_t o=bclen-p1-4;memcpy(bytecode+p1,&o,4);}
|
|
601
|
+
compile_expr();
|
|
602
|
+
if(tp<tn&&tks[tp].type==T_THEN)tp++;
|
|
603
|
+
if(tp<tn&&tks[tp].type==T_COLON)tp++;
|
|
604
|
+
bc(OP_JUMP_IFNOT);p1=bclen;bc32(0);
|
|
605
|
+
}else{
|
|
606
|
+
// Plain ELSE:
|
|
607
|
+
if(tp<tn&&tks[tp].type==T_COLON)tp++;
|
|
608
|
+
bc(OP_JUMP);int p2=bclen;bc32(0);
|
|
609
|
+
{int32_t o=bclen-p1-4;memcpy(bytecode+p1,&o,4);}
|
|
610
|
+
p1=p2; // else block ends at p2
|
|
611
|
+
}
|
|
612
|
+
compile_block();
|
|
613
|
+
}
|
|
614
|
+
// Patch JUMP_IFNOT (fall-through) to END
|
|
615
|
+
{int32_t o=bclen-p1-4;memcpy(bytecode+p1,&o,4);}
|
|
616
|
+
// Patch all ELSEIF skip-to-end JUMPs
|
|
617
|
+
for(int ei=0;ei<end_patch_count;ei++){
|
|
618
|
+
int32_t o=bclen-end_patches[ei]-4;memcpy(bytecode+end_patches[ei],&o,4);
|
|
619
|
+
}
|
|
620
|
+
if(tp<tn&&tks[tp].type==T_END)tp++;
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if(t->type==T_WHILE){
|
|
624
|
+
tp++;
|
|
625
|
+
int loop=bclen;
|
|
626
|
+
// Push loop info for BREAK/CONTINUE
|
|
627
|
+
loop_stack[loop_sp].break_count=0;
|
|
628
|
+
loop_stack[loop_sp].continue_count=0;
|
|
629
|
+
loop_sp++;
|
|
630
|
+
compile_expr();
|
|
631
|
+
if(tp<tn&&tks[tp].type==T_THEN)tp++;
|
|
632
|
+
if(tp<tn&&tks[tp].type==T_COLON)tp++;
|
|
633
|
+
bc(OP_JUMP_IFNOT);int p1=bclen;bc32(0);
|
|
634
|
+
compile_block();
|
|
635
|
+
bc(OP_JUMP);bc32(loop-bclen-4);
|
|
636
|
+
{int32_t o=bclen-p1-4;memcpy(bytecode+p1,&o,4);}
|
|
637
|
+
// Patch all BREAK jumps to here
|
|
638
|
+
loop_sp--;
|
|
639
|
+
for(int bi=0;bi<loop_stack[loop_sp].break_count;bi++){
|
|
640
|
+
int patch=loop_stack[loop_sp].break_patches[bi];
|
|
641
|
+
int32_t o=bclen-patch-4;memcpy(bytecode+patch,&o,4);
|
|
642
|
+
}
|
|
643
|
+
// Patch all CONTINUE jumps to loop start
|
|
644
|
+
for(int ci=0;ci<loop_stack[loop_sp].continue_count;ci++){
|
|
645
|
+
int patch=loop_stack[loop_sp].continue_patches[ci];
|
|
646
|
+
int32_t o=loop-patch-4;memcpy(bytecode+patch,&o,4);
|
|
647
|
+
}
|
|
648
|
+
if(tp<tn&&tks[tp].type==T_END)tp++;
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
if(t->type==T_FOR){
|
|
652
|
+
tp++;
|
|
653
|
+
if(tp<tn&&tks[tp].type==T_IDENT){
|
|
654
|
+
int idx=var_idx(tks[tp].txt);tp++;
|
|
655
|
+
if(tp<tn&&tks[tp].type==T_ASSIGN)tp++;
|
|
656
|
+
compile_expr(); // start
|
|
657
|
+
bc(OP_STORE);bc32(idx);
|
|
658
|
+
if(tp<tn&&tks[tp].type==T_TO)tp++;
|
|
659
|
+
compile_expr(); // end
|
|
660
|
+
int end_idx=var_idx("__for_end");
|
|
661
|
+
bc(OP_STORE);bc32(end_idx);
|
|
662
|
+
// Optional STEP
|
|
663
|
+
int step_idx=-1;
|
|
664
|
+
if(tp<tn&&tks[tp].type==T_STEP){
|
|
665
|
+
tp++;
|
|
666
|
+
compile_expr(); // step value
|
|
667
|
+
step_idx=var_idx("__for_step");
|
|
668
|
+
bc(OP_STORE);bc32(step_idx);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
int loop=bclen;
|
|
672
|
+
loop_stack[loop_sp].break_count=0;
|
|
673
|
+
loop_stack[loop_sp].continue_count=0;
|
|
674
|
+
loop_sp++;
|
|
675
|
+
bc(OP_LOAD);bc32(idx);
|
|
676
|
+
bc(OP_LOAD);bc32(end_idx);
|
|
677
|
+
bc(OP_LTE); // current <= end
|
|
678
|
+
bc(OP_JUMP_IFNOT);int p1=bclen;bc32(0);
|
|
679
|
+
// body
|
|
680
|
+
if(tp<tn&&tks[tp].type==T_COLON)tp++;
|
|
681
|
+
compile_block();
|
|
682
|
+
// Record increment position for CONTINUE patching
|
|
683
|
+
int increment_pos=bclen;
|
|
684
|
+
// increment
|
|
685
|
+
bc(OP_LOAD);bc32(idx);
|
|
686
|
+
if(step_idx>=0){
|
|
687
|
+
bc(OP_LOAD);bc32(step_idx);
|
|
688
|
+
}else{
|
|
689
|
+
bc(OP_PUSH_INT);bc64(1);
|
|
690
|
+
}
|
|
691
|
+
bc(OP_ADD);
|
|
692
|
+
bc(OP_STORE);bc32(idx);
|
|
693
|
+
bc(OP_JUMP);bc32(loop-bclen-4);
|
|
694
|
+
{int32_t o=bclen-p1-4;memcpy(bytecode+p1,&o,4);}
|
|
695
|
+
// Patch all BREAK jumps
|
|
696
|
+
loop_sp--;
|
|
697
|
+
for(int bi=0;bi<loop_stack[loop_sp].break_count;bi++){
|
|
698
|
+
int patch=loop_stack[loop_sp].break_patches[bi];
|
|
699
|
+
int32_t o=bclen-patch-4;memcpy(bytecode+patch,&o,4);
|
|
700
|
+
}
|
|
701
|
+
// Patch all CONTINUE jumps to increment step
|
|
702
|
+
for(int ci=0;ci<loop_stack[loop_sp].continue_count;ci++){
|
|
703
|
+
int patch=loop_stack[loop_sp].continue_patches[ci];
|
|
704
|
+
int32_t o=increment_pos-patch-4;memcpy(bytecode+patch,&o,4);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if(tp<tn&&tks[tp].type==T_END)tp++;
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if(t->type==T_FUNC){
|
|
711
|
+
tp++;
|
|
712
|
+
// Parse: FUNC! name param1 param2 ... : body END!
|
|
713
|
+
if(tp<tn && tks[tp].type==T_IDENT) {
|
|
714
|
+
char fname[64]; strncpy(fname, tks[tp].txt, 63); fname[63]=0;
|
|
715
|
+
int fi = ft_add(fname, bclen);
|
|
716
|
+
func_table[fi].entry_varcount = varcount;
|
|
717
|
+
tp++;
|
|
718
|
+
// Parse parameters
|
|
719
|
+
while(tp<tn && tks[tp].type!=T_COLON && tks[tp].type!=T_END && tks[tp].type!=T_EOF) {
|
|
720
|
+
if(tks[tp].type==T_IDENT) {
|
|
721
|
+
ft_add_param(fi, tks[tp].txt);
|
|
722
|
+
}
|
|
723
|
+
tp++;
|
|
724
|
+
}
|
|
725
|
+
if(tp<tn && tks[tp].type==T_COLON) tp++;
|
|
726
|
+
// Emit JUMP over body
|
|
727
|
+
bc(OP_JUMP); int skip_patch=bclen; bc32(0);
|
|
728
|
+
// Record correct function address (after the JUMP)
|
|
729
|
+
func_table[fi].addr = bclen;
|
|
730
|
+
// Compile body
|
|
731
|
+
compile_block();
|
|
732
|
+
// Record local variable count for this function
|
|
733
|
+
func_table[fi].local_count = varcount - func_table[fi].entry_varcount;
|
|
734
|
+
// Implicit return 0
|
|
735
|
+
bc(OP_PUSH_INT); bc64(0); bc(OP_RET);
|
|
736
|
+
// Patch JUMP to skip over body
|
|
737
|
+
{int32_t o=bclen-skip_patch-4; memcpy(bytecode+skip_patch, &o, 4);}
|
|
738
|
+
if(tp<tn && tks[tp].type==T_END) tp++;
|
|
739
|
+
}
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
if(t->type==T_RETURN){
|
|
743
|
+
tp++;
|
|
744
|
+
if(tp<tn&&tks[tp].type!=T_END&&tks[tp].type!=T_EOF)compile_expr();
|
|
745
|
+
else bc(OP_PUSH_INT),bc64(0);
|
|
746
|
+
bc(OP_RET);return;
|
|
747
|
+
}
|
|
748
|
+
// Expression statement
|
|
749
|
+
compile_expr();
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
static void compile_block(void) {
|
|
753
|
+
while(tp<tn&&tks[tp].type!=T_END&&tks[tp].type!=T_ELSE&&tks[tp].type!=T_ELSEIF&&tks[tp].type!=T_CATCH&&tks[tp].type!=T_FINALLY&&tks[tp].type!=T_EOF)compile_stmt();
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// ============================================================================
|
|
757
|
+
// VM EXECUTION
|
|
758
|
+
// ============================================================================
|
|
759
|
+
|
|
760
|
+
#define VM_STACK_SIZE 4096
|
|
761
|
+
#define VM_VAR_SIZE 4096
|
|
762
|
+
#define VM_CALL_STACK_SIZE 256
|
|
763
|
+
#define VM_MAX_INSTRUCTIONS 2000000000LL
|
|
764
|
+
|
|
765
|
+
static Value vm_stack[VM_STACK_SIZE];
|
|
766
|
+
static int vm_sp=0;
|
|
767
|
+
static Value vm_vars[VM_VAR_SIZE];
|
|
768
|
+
static int vm_var_sp=0;
|
|
769
|
+
static int vm_var_base=0;
|
|
770
|
+
static int vm_running=1;
|
|
771
|
+
static int g_argc=0;
|
|
772
|
+
static char** g_argv=NULL;
|
|
773
|
+
|
|
774
|
+
// Error state
|
|
775
|
+
static char vm_error_msg[256]= {0};
|
|
776
|
+
static int vm_has_error=0;
|
|
777
|
+
|
|
778
|
+
// VM exception handler stack
|
|
779
|
+
typedef struct { int handler_ip; int saved_sp; int saved_call_sp; } VMTryFrame;
|
|
780
|
+
static VMTryFrame vm_try_stack[32];
|
|
781
|
+
static int vm_try_sp=0;
|
|
782
|
+
|
|
783
|
+
typedef struct { int ret_ip; int var_base; int var_sp; int saved_var_idx[256]; Value saved_vars[256]; int saved_count; } VMCallFrame;
|
|
784
|
+
static VMCallFrame vm_call_stack[VM_CALL_STACK_SIZE];
|
|
785
|
+
static int vm_call_sp=0;
|
|
786
|
+
|
|
787
|
+
// VM error handling
|
|
788
|
+
static int vm_handler_ip = -1; // Set by OP_TRY, checked by errors
|
|
789
|
+
static void vm_set_error(const char* msg) {
|
|
790
|
+
snprintf(vm_error_msg, 256, "%s", msg);
|
|
791
|
+
// Check if we have a try handler
|
|
792
|
+
if(vm_try_sp > 0) {
|
|
793
|
+
vm_try_sp--;
|
|
794
|
+
vm_sp = vm_try_stack[vm_try_sp].saved_sp;
|
|
795
|
+
vm_call_sp = vm_try_stack[vm_try_sp].saved_call_sp;
|
|
796
|
+
// Push error message on stack
|
|
797
|
+
vm_stack[vm_sp++] = val_string_v(msg);
|
|
798
|
+
// Signal to vm_exec to jump
|
|
799
|
+
vm_handler_ip = vm_try_stack[vm_try_sp].handler_ip;
|
|
800
|
+
vm_has_error = 0; // Clear error so VM continues
|
|
801
|
+
} else {
|
|
802
|
+
vm_has_error = 1;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
static void vm_push(Value v) {
|
|
807
|
+
if(vm_sp >= VM_STACK_SIZE) {
|
|
808
|
+
vm_set_error("Stack overflow");
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
vm_stack[vm_sp++] = v;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
static Value vm_pop(void) {
|
|
815
|
+
if(vm_sp <= 0) {
|
|
816
|
+
vm_set_error("Stack underflow");
|
|
817
|
+
return val_null_v();
|
|
818
|
+
}
|
|
819
|
+
return vm_stack[--vm_sp];
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
static void vm_exec(uint8_t* code, int len, char strtable[][256], int strcount) {
|
|
823
|
+
int ip=0;
|
|
824
|
+
long long ins_count=0;
|
|
825
|
+
vm_has_error = 0;
|
|
826
|
+
vm_error_msg[0] = 0;
|
|
827
|
+
vm_handler_ip = -1;
|
|
828
|
+
|
|
829
|
+
while(ip<len && !vm_has_error){
|
|
830
|
+
// Check if we need to jump to exception handler
|
|
831
|
+
if(vm_handler_ip >= 0) {
|
|
832
|
+
ip = vm_handler_ip;
|
|
833
|
+
vm_handler_ip = -1;
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
uint8_t op=code[ip];
|
|
837
|
+
ins_count++;
|
|
838
|
+
if(ins_count>VM_MAX_INSTRUCTIONS) {
|
|
839
|
+
fprintf(stderr, "Runtime error: instruction limit exceeded (%lld instructions)\n", VM_MAX_INSTRUCTIONS);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
ip++;
|
|
843
|
+
switch(op){
|
|
844
|
+
case OP_PUSH_INT:{int64_t v;memcpy(&v,code+ip,8);ip+=8;vm_push(val_int_v(v));}break;
|
|
845
|
+
case OP_PUSH_STR:{int32_t si;memcpy(&si,code+ip,4);ip+=4;
|
|
846
|
+
if(si>=0 && si<strcount) vm_push(val_string_v(strtable[si]));
|
|
847
|
+
else { vm_set_error("Invalid string index"); return; }
|
|
848
|
+
}break;
|
|
849
|
+
case OP_PUSH_BOOL:{uint8_t b=code[ip++];vm_push(val_bool_v(b));}break;
|
|
850
|
+
case OP_PUSH_NULL:{vm_push(val_null_v());}break;
|
|
851
|
+
case OP_ADD:{Value b=vm_pop(),a=vm_pop();
|
|
852
|
+
if(a.type==VAL_ARRAY&&b.type==VAL_ARRAY){
|
|
853
|
+
ValueArray*aa=a.as.a,*ba=b.as.a;
|
|
854
|
+
int nc=aa->count+ba->count;
|
|
855
|
+
ValueArray*result=calloc(1,sizeof(ValueArray));
|
|
856
|
+
result->cap=nc>0?nc:8;result->items=calloc(result->cap,sizeof(Value));result->count=0;
|
|
857
|
+
for(int i=0;i<aa->count;i++)result->items[result->count++]=val_copy(aa->items[i]);
|
|
858
|
+
for(int i=0;i<ba->count;i++)result->items[result->count++]=val_copy(ba->items[i]);
|
|
859
|
+
Value vr={VAL_ARRAY};vr.as.a=result;vm_push(vr);val_free(a);val_free(b);
|
|
860
|
+
} else if(a.type==VAL_ARRAY){
|
|
861
|
+
ValueArray*aa=a.as.a;
|
|
862
|
+
int nc=aa->count+1;
|
|
863
|
+
ValueArray*result=calloc(1,sizeof(ValueArray));
|
|
864
|
+
result->cap=nc;result->items=calloc(result->cap,sizeof(Value));result->count=0;
|
|
865
|
+
for(int i=0;i<aa->count;i++)result->items[result->count++]=val_copy(aa->items[i]);
|
|
866
|
+
result->items[result->count++]=val_copy(b);
|
|
867
|
+
Value vr={VAL_ARRAY};vr.as.a=result;vm_push(vr);val_free(a);val_free(b);
|
|
868
|
+
} else if(b.type==VAL_ARRAY){
|
|
869
|
+
ValueArray*ba=b.as.a;
|
|
870
|
+
int nc=1+ba->count;
|
|
871
|
+
ValueArray*result=calloc(1,sizeof(ValueArray));
|
|
872
|
+
result->cap=nc;result->items=calloc(result->cap,sizeof(Value));result->count=0;
|
|
873
|
+
result->items[result->count++]=val_copy(a);
|
|
874
|
+
for(int i=0;i<ba->count;i++)result->items[result->count++]=val_copy(ba->items[i]);
|
|
875
|
+
Value vr={VAL_ARRAY};vr.as.a=result;vm_push(vr);val_free(a);val_free(b);
|
|
876
|
+
} else if(a.type==VAL_INT&&b.type==VAL_INT)vm_push(val_int_v(a.as.i+b.as.i));
|
|
877
|
+
else{Value sa=val_to_string(a),sb=val_to_string(b);
|
|
878
|
+
int len2=(int)strlen(sa.as.s)+(int)strlen(sb.as.s)+1;
|
|
879
|
+
char*buf=malloc(len2);strcpy(buf,sa.as.s);strcat(buf,sb.as.s);
|
|
880
|
+
val_free(sa);val_free(sb);val_free(a);val_free(b);
|
|
881
|
+
vm_push(val_string_v(buf));free(buf);}
|
|
882
|
+
}break;
|
|
883
|
+
case OP_SUB:{Value b=vm_pop(),a=vm_pop();
|
|
884
|
+
if(a.type==VAL_INT&&b.type==VAL_INT) vm_push(val_int_v(a.as.i-b.as.i));
|
|
885
|
+
else vm_push(val_int_v(0));
|
|
886
|
+
val_free(a);val_free(b);}break;
|
|
887
|
+
case OP_MUL:{Value b=vm_pop(),a=vm_pop();
|
|
888
|
+
if(a.type==VAL_INT&&b.type==VAL_INT) vm_push(val_int_v(a.as.i*b.as.i));
|
|
889
|
+
else vm_push(val_int_v(0));
|
|
890
|
+
val_free(a);val_free(b);}break;
|
|
891
|
+
case OP_DIV:{Value b=vm_pop(),a=vm_pop();
|
|
892
|
+
if(a.type!=VAL_INT||b.type!=VAL_INT){vm_set_error("Division requires integers");val_free(a);val_free(b);}
|
|
893
|
+
else if(b.as.i==0){vm_set_error("Division by zero");val_free(a);val_free(b);}
|
|
894
|
+
else{vm_push(val_int_v(a.as.i/b.as.i));val_free(a);val_free(b);}
|
|
895
|
+
}break;
|
|
896
|
+
case OP_MOD:{Value b=vm_pop(),a=vm_pop();
|
|
897
|
+
if(a.type!=VAL_INT||b.type!=VAL_INT){vm_set_error("Modulo requires integers");val_free(a);val_free(b);}
|
|
898
|
+
else if(b.as.i==0){vm_set_error("Modulo by zero");val_free(a);val_free(b);}
|
|
899
|
+
else{vm_push(val_int_v(a.as.i%b.as.i));val_free(a);val_free(b);}
|
|
900
|
+
}break;
|
|
901
|
+
case OP_NEG:{Value a=vm_pop();vm_push(val_int_v(-a.as.i));val_free(a);}break;
|
|
902
|
+
case OP_EQ:{Value b=vm_pop(),a=vm_pop();
|
|
903
|
+
int eq=(a.type==b.type)?(
|
|
904
|
+
a.type==VAL_NULL?1:a.type==VAL_INT?a.as.i==b.as.i:
|
|
905
|
+
a.type==VAL_BOOL?a.as.b==b.as.b:a.type==VAL_STRING?strcmp(a.as.s,b.as.s)==0:0):0;
|
|
906
|
+
vm_push(val_bool_v(eq));val_free(a);val_free(b);
|
|
907
|
+
}break;
|
|
908
|
+
case OP_NEQ:{Value b=vm_pop(),a=vm_pop();
|
|
909
|
+
int eq=(a.type==b.type)?(
|
|
910
|
+
a.type==VAL_NULL?1:a.type==VAL_INT?a.as.i==b.as.i:
|
|
911
|
+
a.type==VAL_BOOL?a.as.b==b.as.b:a.type==VAL_STRING?strcmp(a.as.s,b.as.s)==0:0):0;
|
|
912
|
+
vm_push(val_bool_v(!eq));val_free(a);val_free(b);
|
|
913
|
+
}break;
|
|
914
|
+
case OP_LT:{Value b=vm_pop(),a=vm_pop();
|
|
915
|
+
if(a.type==VAL_INT&&b.type==VAL_INT) vm_push(val_bool_v(a.as.i<b.as.i));
|
|
916
|
+
else if(a.type==VAL_STRING&&b.type==VAL_STRING) vm_push(val_bool_v(strcmp(a.as.s,b.as.s)<0));
|
|
917
|
+
else vm_push(val_bool_v(0));
|
|
918
|
+
val_free(a);val_free(b);}break;
|
|
919
|
+
case OP_GT:{Value b=vm_pop(),a=vm_pop();
|
|
920
|
+
if(a.type==VAL_INT&&b.type==VAL_INT) vm_push(val_bool_v(a.as.i>b.as.i));
|
|
921
|
+
else if(a.type==VAL_STRING&&b.type==VAL_STRING) vm_push(val_bool_v(strcmp(a.as.s,b.as.s)>0));
|
|
922
|
+
else vm_push(val_bool_v(0));
|
|
923
|
+
val_free(a);val_free(b);}break;
|
|
924
|
+
case OP_LTE:{Value b=vm_pop(),a=vm_pop();
|
|
925
|
+
if(a.type==VAL_INT&&b.type==VAL_INT) vm_push(val_bool_v(a.as.i<=b.as.i));
|
|
926
|
+
else vm_push(val_bool_v(0));
|
|
927
|
+
val_free(a);val_free(b);}break;
|
|
928
|
+
case OP_GTE:{Value b=vm_pop(),a=vm_pop();
|
|
929
|
+
if(a.type==VAL_INT&&b.type==VAL_INT) vm_push(val_bool_v(a.as.i>=b.as.i));
|
|
930
|
+
else vm_push(val_bool_v(0));
|
|
931
|
+
val_free(a);val_free(b);}break;
|
|
932
|
+
case OP_AND:{Value b=vm_pop(),a=vm_pop();vm_push(val_bool_v(val_truthy(a)&&val_truthy(b)));val_free(a);val_free(b);}break;
|
|
933
|
+
case OP_OR:{Value b=vm_pop(),a=vm_pop();vm_push(val_bool_v(val_truthy(a)||val_truthy(b)));val_free(a);val_free(b);}break;
|
|
934
|
+
case OP_NOT:{Value a=vm_pop();vm_push(val_bool_v(!val_truthy(a)));val_free(a);}break;
|
|
935
|
+
case OP_BITAND:{Value b=vm_pop(),a=vm_pop();vm_push(val_int_v(a.as.i&b.as.i));val_free(a);val_free(b);}break;
|
|
936
|
+
case OP_BITOR:{Value b=vm_pop(),a=vm_pop();vm_push(val_int_v(a.as.i|b.as.i));val_free(a);val_free(b);}break;
|
|
937
|
+
case OP_LSHIFT:{Value b=vm_pop(),a=vm_pop();vm_push(val_int_v(a.as.i<<(int)b.as.i));val_free(a);val_free(b);}break;
|
|
938
|
+
case OP_RSHIFT:{Value b=vm_pop(),a=vm_pop();vm_push(val_int_v(a.as.i>>(int)b.as.i));val_free(a);val_free(b);}break;
|
|
939
|
+
case OP_STORE:{int32_t idx;memcpy(&idx,code+ip,4);ip+=4;
|
|
940
|
+
int vi=vm_var_base+idx;
|
|
941
|
+
if(vi<0||vi>=VM_VAR_SIZE){vm_set_error("Variable index out of bounds");break;}
|
|
942
|
+
val_free(vm_vars[vi]);vm_vars[vi]=vm_pop();
|
|
943
|
+
}break;
|
|
944
|
+
case OP_LOAD:{int32_t idx;memcpy(&idx,code+ip,4);ip+=4;
|
|
945
|
+
int vi=vm_var_base+idx;
|
|
946
|
+
if(vi<0||vi>=VM_VAR_SIZE){vm_set_error("Variable index out of bounds");break;}
|
|
947
|
+
Value v=vm_vars[vi];
|
|
948
|
+
if(v.type==VAL_STRING)vm_push(val_string_v(v.as.s));
|
|
949
|
+
else if(v.type==VAL_ARRAY){
|
|
950
|
+
ValueArray* src=v.as.a;
|
|
951
|
+
ValueArray* dst=calloc(1,sizeof(ValueArray));
|
|
952
|
+
dst->cap=src->count>0?src->count:8;
|
|
953
|
+
dst->items=calloc(dst->cap,sizeof(Value));
|
|
954
|
+
dst->count=src->count;
|
|
955
|
+
for(int i=0;i<src->count;i++)dst->items[i]=val_copy(src->items[i]);
|
|
956
|
+
Value rv={VAL_ARRAY};rv.as.a=dst;vm_push(rv);
|
|
957
|
+
} else vm_push(v);
|
|
958
|
+
}break;
|
|
959
|
+
case OP_PRINT:{Value v=vm_pop();Value s=val_to_string(v);
|
|
960
|
+
printf("%s\n",s.as.s);
|
|
961
|
+
val_free(s);val_free(v);
|
|
962
|
+
}break;
|
|
963
|
+
case OP_JUMP:{int32_t off;memcpy(&off,code+ip,4);ip+=4;ip+=off;}break;
|
|
964
|
+
case OP_JUMP_IFNOT:{int32_t off;memcpy(&off,code+ip,4);ip+=4;Value v=vm_pop();
|
|
965
|
+
if(!val_truthy(v))ip+=off;
|
|
966
|
+
val_free(v);}break;
|
|
967
|
+
case OP_ARRAY_NEW:{int32_t count;memcpy(&count,code+ip,4);ip+=4;
|
|
968
|
+
Value arr={VAL_ARRAY};arr.as.a=calloc(1,sizeof(ValueArray));
|
|
969
|
+
arr.as.a->cap=count>0?count:8;arr.as.a->items=calloc(arr.as.a->cap,sizeof(Value));arr.as.a->count=0;
|
|
970
|
+
for(int i=0;i<count;i++)arr.as.a->items[arr.as.a->count++]=vm_pop();
|
|
971
|
+
// Reverse
|
|
972
|
+
for(int i=0;i<arr.as.a->count/2;i++){Value tmp=arr.as.a->items[i];arr.as.a->items[i]=arr.as.a->items[arr.as.a->count-1-i];arr.as.a->items[arr.as.a->count-1-i]=tmp;}
|
|
973
|
+
vm_push(arr);
|
|
974
|
+
}break;
|
|
975
|
+
case OP_ARRAY_GET:{Value idx=vm_pop(),arr=vm_pop();
|
|
976
|
+
if(arr.type==VAL_ARRAY && idx.type==VAL_INT){
|
|
977
|
+
int i=(int)idx.as.i;
|
|
978
|
+
if(i>=0 && i<arr.as.a->count){
|
|
979
|
+
Value v=arr.as.a->items[i];
|
|
980
|
+
if(v.type==VAL_STRING) vm_push(val_string_v(v.as.s));
|
|
981
|
+
else vm_push(v);
|
|
982
|
+
} else { vm_push(val_null_v()); }
|
|
983
|
+
} else { vm_push(val_null_v()); }
|
|
984
|
+
val_free(arr);val_free(idx);
|
|
985
|
+
}break;
|
|
986
|
+
case OP_TOSTR:{Value v=vm_pop();Value s=val_to_string(v);val_free(v);vm_push(s);}break;
|
|
987
|
+
case OP_TONUM:{Value v=vm_pop();
|
|
988
|
+
if(v.type==VAL_STRING){int64_t n=atoll(v.as.s);val_free(v);vm_push(val_int_v(n));}
|
|
989
|
+
else if(v.type==VAL_INT){vm_push(v);}
|
|
990
|
+
else{val_free(v);vm_push(val_int_v(0));}
|
|
991
|
+
}break;
|
|
992
|
+
case OP_TYPEOF:{Value v=vm_pop();const char*tn2=val_type_name(v);vm_push(val_string_v(tn2));val_free(v);}break;
|
|
993
|
+
case OP_ARRAY_LEN:{Value v=vm_pop();
|
|
994
|
+
if(v.type==VAL_ARRAY){int c=v.as.a->count;vm_push(val_int_v(c));val_free(v);}
|
|
995
|
+
else if(v.type==VAL_STRING){int c=(int)strlen(v.as.s);vm_push(val_int_v(c));val_free(v);}
|
|
996
|
+
else{val_free(v);vm_push(val_int_v(0));}
|
|
997
|
+
}break;
|
|
998
|
+
case OP_ABS:{Value v=vm_pop();if(v.type==VAL_INT&&v.as.i<0)v.as.i=-v.as.i;vm_push(v);}break;
|
|
999
|
+
case OP_MIN:{Value b=vm_pop(),a=vm_pop();
|
|
1000
|
+
int64_t va=a.type==VAL_INT?a.as.i:0, vb=b.type==VAL_INT?b.as.i:0;
|
|
1001
|
+
vm_push(val_int_v(va<vb?va:vb));val_free(a);val_free(b);}break;
|
|
1002
|
+
case OP_MAX:{Value b=vm_pop(),a=vm_pop();
|
|
1003
|
+
int64_t va=a.type==VAL_INT?a.as.i:0, vb=b.type==VAL_INT?b.as.i:0;
|
|
1004
|
+
vm_push(val_int_v(va>vb?va:vb));val_free(a);val_free(b);}break;
|
|
1005
|
+
case OP_SQRT:{Value v=vm_pop();
|
|
1006
|
+
double d=v.type==VAL_INT?(double)v.as.i:0;
|
|
1007
|
+
vm_push(val_int_v((int64_t)sqrt(d)));val_free(v);}break;
|
|
1008
|
+
case OP_POW:{Value b=vm_pop(),a=vm_pop();
|
|
1009
|
+
double base=a.type==VAL_INT?(double)a.as.i:0;
|
|
1010
|
+
double exp=b.type==VAL_INT?(double)b.as.i:0;
|
|
1011
|
+
vm_push(val_int_v((int64_t)pow(base,exp)));val_free(a);val_free(b);}break;
|
|
1012
|
+
case OP_FLOOR:{Value v=vm_pop();
|
|
1013
|
+
if(v.type==VAL_INT)vm_push(v);
|
|
1014
|
+
else{val_free(v);vm_push(val_int_v(0));}}break;
|
|
1015
|
+
case OP_CEIL:{Value v=vm_pop();
|
|
1016
|
+
if(v.type==VAL_INT)vm_push(v);
|
|
1017
|
+
else{val_free(v);vm_push(val_int_v(0));}}break;
|
|
1018
|
+
case OP_ROUND:{Value v=vm_pop();
|
|
1019
|
+
if(v.type==VAL_INT)vm_push(v);
|
|
1020
|
+
else{val_free(v);vm_push(val_int_v(0));}}break;
|
|
1021
|
+
case OP_READ_FILE:{Value v=vm_pop();
|
|
1022
|
+
if(v.type==VAL_STRING){
|
|
1023
|
+
FILE*f=fopen(v.as.s,"rb");
|
|
1024
|
+
if(f){fseek(f,0,SEEK_END);long fsize=ftell(f);
|
|
1025
|
+
if(fsize<0){fclose(f);vm_push(val_string_v(""));}
|
|
1026
|
+
else{fseek(f,0,SEEK_SET);
|
|
1027
|
+
char*buf=malloc(fsize+1);size_t read=fread(buf,1,fsize,f);buf[read]=0;fclose(f);
|
|
1028
|
+
vm_push(val_string_v(buf));free(buf);}}
|
|
1029
|
+
else{vm_set_error("Cannot open file");vm_push(val_string_v(""));}
|
|
1030
|
+
}else{val_free(v);vm_set_error("READ_FILE requires string argument");vm_push(val_string_v(""));}}break;
|
|
1031
|
+
case OP_WRITE_FILE:{Value v2=vm_pop(),v1=vm_pop();
|
|
1032
|
+
if(v1.type==VAL_STRING&&v2.type==VAL_STRING){
|
|
1033
|
+
FILE*f=fopen(v1.as.s,"wb");
|
|
1034
|
+
if(f){fwrite(v2.as.s,1,strlen(v2.as.s),f);fclose(f);}
|
|
1035
|
+
else{vm_set_error("Cannot write file");}
|
|
1036
|
+
}
|
|
1037
|
+
val_free(v1);val_free(v2);vm_push(val_int_v(0));}break;
|
|
1038
|
+
case OP_WRITE_BYTES:{Value arr=vm_pop(),fn=vm_pop();
|
|
1039
|
+
if(fn.type==VAL_STRING&&arr.type==VAL_ARRAY){
|
|
1040
|
+
FILE*f=fopen(fn.as.s,"wb");
|
|
1041
|
+
if(f){
|
|
1042
|
+
for(int i=0;i<arr.as.a->count;i++){
|
|
1043
|
+
uint8_t b=(uint8_t)(arr.as.a->items[i].as.i&0xFF);
|
|
1044
|
+
fwrite(&b,1,1,f);
|
|
1045
|
+
}
|
|
1046
|
+
fclose(f);
|
|
1047
|
+
} else { vm_set_error("Cannot write file"); }
|
|
1048
|
+
}
|
|
1049
|
+
val_free(fn);val_free(arr);vm_push(val_int_v(0));}break;
|
|
1050
|
+
case OP_ARRAY_PUSH:{Value val=vm_pop(),arr=vm_pop();
|
|
1051
|
+
if(arr.type==VAL_ARRAY){
|
|
1052
|
+
if(arr.as.a->count>=arr.as.a->cap){
|
|
1053
|
+
int newcap=arr.as.a->cap*2;
|
|
1054
|
+
Value* newitems=realloc(arr.as.a->items,newcap*sizeof(Value));
|
|
1055
|
+
if(!newitems){vm_set_error("Out of memory");val_free(val);val_free(arr);break;}
|
|
1056
|
+
arr.as.a->items=newitems;
|
|
1057
|
+
arr.as.a->cap=newcap;
|
|
1058
|
+
}
|
|
1059
|
+
arr.as.a->items[arr.as.a->count++]=val;
|
|
1060
|
+
} else val_free(val);
|
|
1061
|
+
vm_push(arr);
|
|
1062
|
+
}break;
|
|
1063
|
+
case OP_ARRAY_POP:{Value arr=vm_pop();
|
|
1064
|
+
if(arr.type==VAL_ARRAY && arr.as.a->count>0){
|
|
1065
|
+
Value v=arr.as.a->items[--arr.as.a->count];
|
|
1066
|
+
if(v.type==VAL_STRING)vm_push(val_string_v(v.as.s));
|
|
1067
|
+
else if(v.type==VAL_ARRAY) vm_push(val_copy(v));
|
|
1068
|
+
else vm_push(v);
|
|
1069
|
+
} else vm_push(val_null_v());
|
|
1070
|
+
val_free(arr);
|
|
1071
|
+
}break;
|
|
1072
|
+
case OP_ARRAY_SET:{Value val=vm_pop(),idx=vm_pop(),arr=vm_pop();
|
|
1073
|
+
if(arr.type==VAL_ARRAY && idx.type==VAL_INT){
|
|
1074
|
+
int i=(int)idx.as.i;
|
|
1075
|
+
if(i>=0 && i<arr.as.a->count){val_free(arr.as.a->items[i]);arr.as.a->items[i]=val;}
|
|
1076
|
+
else val_free(val);
|
|
1077
|
+
} else val_free(val);
|
|
1078
|
+
vm_push(arr);
|
|
1079
|
+
}break;
|
|
1080
|
+
case OP_SUBSTR:{Value len=vm_pop(),start=vm_pop(),str=vm_pop();
|
|
1081
|
+
if(str.type==VAL_STRING && start.type==VAL_INT && len.type==VAL_INT){
|
|
1082
|
+
int s=(int)start.as.i, l=(int)len.as.i, slen=(int)strlen(str.as.s);
|
|
1083
|
+
if(s<0)s=0; if(s>slen)s=slen; if(s+l>slen)l=slen-s;
|
|
1084
|
+
if(l<0)l=0;
|
|
1085
|
+
char*buf=malloc(l+1);memcpy(buf,str.as.s+s,l);buf[l]=0;
|
|
1086
|
+
vm_push(val_string_v(buf));free(buf);
|
|
1087
|
+
} else { vm_push(val_string_v("")); }
|
|
1088
|
+
val_free(str);val_free(start);val_free(len);
|
|
1089
|
+
}break;
|
|
1090
|
+
case OP_CHAR_AT:{Value idx=vm_pop(),str=vm_pop();
|
|
1091
|
+
if(str.type==VAL_STRING && idx.type==VAL_INT){
|
|
1092
|
+
int i=(int)idx.as.i; int slen=(int)strlen(str.as.s);
|
|
1093
|
+
if(i>=0 && i<slen){vm_push(val_int_v((int)(unsigned char)str.as.s[i]));}
|
|
1094
|
+
else vm_push(val_int_v(0));
|
|
1095
|
+
} else vm_push(val_int_v(0));
|
|
1096
|
+
val_free(str);val_free(idx);
|
|
1097
|
+
}break;
|
|
1098
|
+
case OP_TO_UPPER:{Value v=vm_pop();
|
|
1099
|
+
if(v.type==VAL_STRING){char*buf=strdup(v.as.s);for(char*p=buf;*p;p++)*p=(char)toupper((unsigned char)*p);vm_push(val_string_v(buf));free(buf);}
|
|
1100
|
+
else vm_push(v);
|
|
1101
|
+
}break;
|
|
1102
|
+
case OP_TO_LOWER:{Value v=vm_pop();
|
|
1103
|
+
if(v.type==VAL_STRING){char*buf=strdup(v.as.s);for(char*p=buf;*p;p++)*p=(char)tolower((unsigned char)*p);vm_push(val_string_v(buf));free(buf);}
|
|
1104
|
+
else vm_push(v);
|
|
1105
|
+
}break;
|
|
1106
|
+
case OP_STR_EQ:{Value b=vm_pop(),a=vm_pop();
|
|
1107
|
+
int eq=0;
|
|
1108
|
+
if(a.type==VAL_STRING && b.type==VAL_STRING) eq=(strcmp(a.as.s,b.as.s)==0);
|
|
1109
|
+
vm_push(val_bool_v(eq));val_free(a);val_free(b);
|
|
1110
|
+
}break;
|
|
1111
|
+
case OP_DUP:{Value v=vm_stack[vm_sp-1];
|
|
1112
|
+
if(v.type==VAL_STRING) vm_push(val_string_v(v.as.s));
|
|
1113
|
+
else if(v.type==VAL_ARRAY) vm_push(val_copy(v));
|
|
1114
|
+
else vm_push(v);
|
|
1115
|
+
}break;
|
|
1116
|
+
case OP_SWAP:{Value b=vm_pop(),a=vm_pop();vm_push(b);vm_push(a);}break;
|
|
1117
|
+
case OP_EXIT:{int code2=0;if(vm_sp>0){Value v=vm_pop();if(v.type==VAL_INT)code2=(int)v.as.i;val_free(v);}exit(code2);}break;
|
|
1118
|
+
case OP_CHAR:{Value v=vm_pop();
|
|
1119
|
+
if(v.type==VAL_INT){char buf[2]={(char)v.as.i,0};vm_push(val_string_v(buf));}
|
|
1120
|
+
else{val_free(v);vm_push(val_string_v(""));}
|
|
1121
|
+
}break;
|
|
1122
|
+
case OP_ORD:{Value v=vm_pop();
|
|
1123
|
+
if(v.type==VAL_STRING && strlen(v.as.s)>0){vm_push(val_int_v((int)(unsigned char)v.as.s[0]));val_free(v);}
|
|
1124
|
+
else{val_free(v);vm_push(val_int_v(0));}
|
|
1125
|
+
}break;
|
|
1126
|
+
case OP_THROW:{Value v=vm_pop();
|
|
1127
|
+
if(vm_try_sp > 0) {
|
|
1128
|
+
// Set error and let vm_set_error handle the jump
|
|
1129
|
+
vm_set_error(v.type==VAL_STRING ? v.as.s : "Exception thrown");
|
|
1130
|
+
val_free(v);
|
|
1131
|
+
} else {
|
|
1132
|
+
fprintf(stderr, "Uncaught exception: ");
|
|
1133
|
+
if(v.type==VAL_STRING) fprintf(stderr, "%s\n", v.as.s);
|
|
1134
|
+
else { Value s=val_to_string(v); fprintf(stderr, "%s\n", s.as.s); val_free(s); }
|
|
1135
|
+
val_free(v);
|
|
1136
|
+
vm_has_error = 1;
|
|
1137
|
+
}
|
|
1138
|
+
}break;
|
|
1139
|
+
case OP_TRY:{int32_t handler_off;memcpy(&handler_off,code+ip,4);ip+=4;
|
|
1140
|
+
// Push handler onto try stack
|
|
1141
|
+
if(vm_try_sp < 32) {
|
|
1142
|
+
vm_try_stack[vm_try_sp].handler_ip = ip + handler_off;
|
|
1143
|
+
vm_try_stack[vm_try_sp].saved_sp = vm_sp;
|
|
1144
|
+
vm_try_stack[vm_try_sp].saved_call_sp = vm_call_sp;
|
|
1145
|
+
vm_try_sp++;
|
|
1146
|
+
}
|
|
1147
|
+
}break;
|
|
1148
|
+
case OP_CATCH:{/* handled by compiler with JUMP patching */}break;
|
|
1149
|
+
case OP_FINALLY:{/* handled by compiler with JUMP patching */}break;
|
|
1150
|
+
case OP_ENDTRY:{if(vm_try_sp>0)vm_try_sp--;}break;
|
|
1151
|
+
case OP_CALL:{int32_t name_idx;memcpy(&name_idx,code+ip,4);ip+=4;
|
|
1152
|
+
int32_t arity;memcpy(&arity,code+ip,4);ip+=4;
|
|
1153
|
+
if(name_idx<0||name_idx>=strcount){vm_set_error("Invalid function name index");break;}
|
|
1154
|
+
const char* name=strings[name_idx];
|
|
1155
|
+
// Built-in functions
|
|
1156
|
+
if(!strcmp(name,"LEN")){Value v=vm_pop();
|
|
1157
|
+
if(v.type==VAL_STRING){vm_push(val_int_v((int64_t)strlen(v.as.s)));val_free(v);}
|
|
1158
|
+
else if(v.type==VAL_ARRAY){vm_push(val_int_v(v.as.a->count));val_free(v);}
|
|
1159
|
+
else{val_free(v);vm_push(val_int_v(0));}
|
|
1160
|
+
} else if(!strcmp(name,"TYPEOF")){Value v=vm_pop();vm_push(val_string_v(val_type_name(v)));val_free(v);}
|
|
1161
|
+
else if(!strcmp(name,"TO_STRING")){Value v=vm_pop();vm_push(val_to_string(v));val_free(v);}
|
|
1162
|
+
else if(!strcmp(name,"TO_NUMBER")){Value v=vm_pop();
|
|
1163
|
+
if(v.type==VAL_STRING){int64_t n=atoll(v.as.s);val_free(v);vm_push(val_int_v(n));}
|
|
1164
|
+
else if(v.type==VAL_INT){vm_push(v);}
|
|
1165
|
+
else{val_free(v);vm_push(val_int_v(0));}
|
|
1166
|
+
} else if(!strcmp(name,"RANDOM")){for(int i=0;i<arity;i++)val_free(vm_pop());vm_push(val_int_v(rand()%100));}
|
|
1167
|
+
else if(!strcmp(name,"TIME")){for(int i=0;i<arity;i++)val_free(vm_pop());vm_push(val_int_v((int64_t)time(NULL)));}
|
|
1168
|
+
else if(!strcmp(name,"ABS")){Value v=vm_pop();if(v.type==VAL_INT&&v.as.i<0)v.as.i=-v.as.i;vm_push(v);}
|
|
1169
|
+
else if(!strcmp(name,"ARGUMENT_COUNT")){for(int i=0;i<arity;i++)val_free(vm_pop());vm_push(val_int_v(g_argc));}
|
|
1170
|
+
else if(!strcmp(name,"ARGUMENT")){Value v=vm_pop();
|
|
1171
|
+
if(v.type==VAL_INT && v.as.i>=0 && v.as.i<g_argc){
|
|
1172
|
+
vm_push(val_string_v(g_argv[v.as.i]));
|
|
1173
|
+
} else { val_free(v); vm_push(val_string_v("")); }
|
|
1174
|
+
} else if(!strcmp(name,"CHAR")){Value v=vm_pop();
|
|
1175
|
+
if(v.type==VAL_INT){char buf[2]={(char)v.as.i,0};vm_push(val_string_v(buf));}
|
|
1176
|
+
else{val_free(v);vm_push(val_string_v(""));}
|
|
1177
|
+
} else if(!strcmp(name,"ORD")){Value v=vm_pop();
|
|
1178
|
+
if(v.type==VAL_STRING && strlen(v.as.s)>0){vm_push(val_int_v((int)(unsigned char)v.as.s[0]));val_free(v);}
|
|
1179
|
+
else{val_free(v);vm_push(val_int_v(0));}
|
|
1180
|
+
} else if(!strcmp(name,"READ_FILE")){Value v=vm_pop();
|
|
1181
|
+
if(v.type==VAL_STRING){
|
|
1182
|
+
FILE*f=fopen(v.as.s,"rb");
|
|
1183
|
+
if(f){fseek(f,0,SEEK_END);long fsize=ftell(f);
|
|
1184
|
+
if(fsize<0){fclose(f);vm_push(val_string_v(""));}
|
|
1185
|
+
else{fseek(f,0,SEEK_SET);
|
|
1186
|
+
char*buf=malloc(fsize+1);size_t r=fread(buf,1,fsize,f);buf[r]=0;fclose(f);
|
|
1187
|
+
vm_push(val_string_v(buf));free(buf);}}
|
|
1188
|
+
else{vm_set_error("Cannot open file");vm_push(val_string_v(""));}
|
|
1189
|
+
}else{val_free(v);vm_set_error("READ_FILE requires string");vm_push(val_string_v(""));}
|
|
1190
|
+
} else if(!strcmp(name,"WRITE_FILE")){Value v2=vm_pop(),v1=vm_pop();
|
|
1191
|
+
if(v1.type==VAL_STRING&&v2.type==VAL_STRING){
|
|
1192
|
+
FILE*f=fopen(v1.as.s,"wb");
|
|
1193
|
+
if(f){fwrite(v2.as.s,1,strlen(v2.as.s),f);fclose(f);}
|
|
1194
|
+
else{vm_set_error("Cannot write file");}
|
|
1195
|
+
}
|
|
1196
|
+
val_free(v1);val_free(v2);vm_push(val_int_v(0));
|
|
1197
|
+
} else if(!strcmp(name,"WRITE_BYTES")){Value arr=vm_pop(),fn=vm_pop();
|
|
1198
|
+
if(fn.type==VAL_STRING&&arr.type==VAL_ARRAY){
|
|
1199
|
+
FILE*f=fopen(fn.as.s,"wb");
|
|
1200
|
+
if(f){
|
|
1201
|
+
for(int i=0;i<arr.as.a->count;i++){
|
|
1202
|
+
uint8_t b=(uint8_t)(arr.as.a->items[i].as.i&0xFF);
|
|
1203
|
+
fwrite(&b,1,1,f);
|
|
1204
|
+
}
|
|
1205
|
+
fclose(f);
|
|
1206
|
+
} else { vm_set_error("Cannot write file"); }
|
|
1207
|
+
}
|
|
1208
|
+
val_free(fn);val_free(arr);vm_push(val_int_v(0));
|
|
1209
|
+
} else {
|
|
1210
|
+
// User-defined function
|
|
1211
|
+
int fi=ft_find(name);
|
|
1212
|
+
if(fi<0){
|
|
1213
|
+
char errmsg[256];snprintf(errmsg,256,"Undefined function '%s'",name);
|
|
1214
|
+
vm_set_error(errmsg);break;
|
|
1215
|
+
}
|
|
1216
|
+
if(vm_call_sp>=VM_CALL_STACK_SIZE){vm_set_error("Call stack overflow");break;}
|
|
1217
|
+
// Save return address + old var_base
|
|
1218
|
+
vm_call_stack[vm_call_sp].ret_ip=ip;
|
|
1219
|
+
vm_call_stack[vm_call_sp].var_base=vm_var_base;
|
|
1220
|
+
vm_call_stack[vm_call_sp].var_sp=vm_var_sp;
|
|
1221
|
+
vm_call_stack[vm_call_sp].saved_count=0;
|
|
1222
|
+
// Save param slots and local variable slots
|
|
1223
|
+
int sc=0;
|
|
1224
|
+
// Mark which indices have been saved (to avoid duplicates)
|
|
1225
|
+
int saved_flag[256]={0};
|
|
1226
|
+
// 1) Save param slots (will be overwritten by args)
|
|
1227
|
+
int pcount=func_table[fi].param_count<arity?func_table[fi].param_count:arity;
|
|
1228
|
+
for(int i=0;i<pcount;i++){
|
|
1229
|
+
const char* pname=func_table[fi].params[i];
|
|
1230
|
+
int gidx=var_idx(pname);
|
|
1231
|
+
if(gidx>=0 && gidx<256 && !saved_flag[gidx] && sc<256){
|
|
1232
|
+
vm_call_stack[vm_call_sp].saved_var_idx[sc]=gidx;
|
|
1233
|
+
vm_call_stack[vm_call_sp].saved_vars[sc]=vm_vars[gidx];
|
|
1234
|
+
vm_call_stack[vm_call_sp].saved_count=++sc;
|
|
1235
|
+
saved_flag[gidx]=1;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
// 2) Save local variables (new names created inside function body)
|
|
1239
|
+
int ev=func_table[fi].entry_varcount;
|
|
1240
|
+
int lc=func_table[fi].local_count;
|
|
1241
|
+
for(int i=ev;i<ev+lc && i<256;i++){
|
|
1242
|
+
if(!saved_flag[i] && sc<256){
|
|
1243
|
+
vm_call_stack[vm_call_sp].saved_var_idx[sc]=i;
|
|
1244
|
+
vm_call_stack[vm_call_sp].saved_vars[sc]=vm_vars[i];
|
|
1245
|
+
vm_call_stack[vm_call_sp].saved_count=++sc;
|
|
1246
|
+
saved_flag[i]=1;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
// Assign params in REVERSE order so params[0] gets leftmost arg
|
|
1250
|
+
for(int i=pcount-1;i>=0;i--){
|
|
1251
|
+
const char* pname=func_table[fi].params[i];
|
|
1252
|
+
int gidx=var_idx(pname);
|
|
1253
|
+
if(gidx>=0 && gidx<VM_VAR_SIZE){
|
|
1254
|
+
vm_vars[gidx]=vm_pop();
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
vm_call_sp++;
|
|
1258
|
+
// Discard remaining args
|
|
1259
|
+
for(int i=arity;i>func_table[fi].param_count;i--)val_free(vm_pop());
|
|
1260
|
+
// Jump to function
|
|
1261
|
+
ip=func_table[fi].addr;
|
|
1262
|
+
}
|
|
1263
|
+
}break;
|
|
1264
|
+
case OP_RET:{Value v=vm_pop();
|
|
1265
|
+
if(vm_call_sp>0){vm_call_sp--;
|
|
1266
|
+
// Restore saved global variable slots
|
|
1267
|
+
for(int i=0;i<vm_call_stack[vm_call_sp].saved_count;i++){
|
|
1268
|
+
int gidx=vm_call_stack[vm_call_sp].saved_var_idx[i];
|
|
1269
|
+
if(gidx>=0 && gidx<VM_VAR_SIZE){
|
|
1270
|
+
val_free(vm_vars[gidx]);
|
|
1271
|
+
vm_vars[gidx]=vm_call_stack[vm_call_sp].saved_vars[i];
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
ip=vm_call_stack[vm_call_sp].ret_ip;
|
|
1275
|
+
vm_var_base=vm_call_stack[vm_call_sp].var_base;
|
|
1276
|
+
vm_var_sp=vm_call_stack[vm_call_sp].var_sp;}
|
|
1277
|
+
else{vm_push(v);return;}
|
|
1278
|
+
vm_push(v);
|
|
1279
|
+
}break;
|
|
1280
|
+
case OP_HALT:return;
|
|
1281
|
+
default:
|
|
1282
|
+
fprintf(stderr, "Runtime error: unknown opcode 0x%02X at ip=%d\n", op, ip-1);
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
if(vm_has_error) {
|
|
1287
|
+
fprintf(stderr, "Runtime error: %s\n", vm_error_msg);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// ============================================================================
|
|
1292
|
+
// CLI
|
|
1293
|
+
// ============================================================================
|
|
1294
|
+
|
|
1295
|
+
static void print_version(void) {
|
|
1296
|
+
fprintf(stderr, "Nebulara v2.0 - AI-Native Programming Language\n");
|
|
1297
|
+
fprintf(stderr, "Usage:\n");
|
|
1298
|
+
fprintf(stderr, " nebulara run <file.nbs> Execute a .nbs file\n");
|
|
1299
|
+
fprintf(stderr, " nebulara build <file.nbs> Compile to bytecode\n");
|
|
1300
|
+
fprintf(stderr, " nebulara repl Interactive REPL\n");
|
|
1301
|
+
fprintf(stderr, " nebulara version Show version\n");
|
|
1302
|
+
fprintf(stderr, " nebulara help Show this help\n");
|
|
1303
|
+
fprintf(stderr, " nebulara highlight <file> Syntax highlight\n");
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
static void cleanup_vm(void) {
|
|
1307
|
+
for (int i = 0; i < VM_VAR_SIZE; i++) val_free(vm_vars[i]);
|
|
1308
|
+
for (int i = 0; i < vm_sp; i++) val_free(vm_stack[i]);
|
|
1309
|
+
memset(vm_vars, 0, sizeof(vm_vars));
|
|
1310
|
+
memset(vm_stack, 0, sizeof(vm_stack));
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
static int run_file(const char* path) {
|
|
1314
|
+
FILE* f = fopen(path, "rb");
|
|
1315
|
+
if (!f) { fprintf(stderr, "Error: cannot open '%s'\n", path); return 1; }
|
|
1316
|
+
fseek(f, 0, SEEK_END);
|
|
1317
|
+
long len = ftell(f);
|
|
1318
|
+
if(len<0){fclose(f);fprintf(stderr,"Error: cannot determine file size\n");return 1;}
|
|
1319
|
+
fseek(f, 0, SEEK_SET);
|
|
1320
|
+
char* src = (char*)malloc(len + 1);
|
|
1321
|
+
if(!src){fclose(f);fprintf(stderr,"Error: out of memory\n");return 1;}
|
|
1322
|
+
size_t read=fread(src, 1, len, f);
|
|
1323
|
+
src[read] = 0;
|
|
1324
|
+
fclose(f);
|
|
1325
|
+
|
|
1326
|
+
// Reset compiler state
|
|
1327
|
+
func_count = 0;
|
|
1328
|
+
|
|
1329
|
+
// Lex
|
|
1330
|
+
Lx lx_state = {src, 0, (int)read, 1};
|
|
1331
|
+
tn = 0; tp = 0;
|
|
1332
|
+
do { tks[tn++] = lx(&lx_state); } while (tks[tn-1].type != T_EOF && tn < 8192);
|
|
1333
|
+
|
|
1334
|
+
// Compile
|
|
1335
|
+
bclen = 0;
|
|
1336
|
+
varcount = 0;
|
|
1337
|
+
strcount = 0;
|
|
1338
|
+
while (tp < tn && tks[tp].type != T_EOF) compile_stmt();
|
|
1339
|
+
bc(OP_HALT);
|
|
1340
|
+
fprintf(stderr, "Compiled %d bytes, %d variables, %d strings, %d functions\n", bclen, varcount, strcount, func_count);
|
|
1341
|
+
|
|
1342
|
+
// Reset VM state
|
|
1343
|
+
cleanup_vm();
|
|
1344
|
+
vm_sp = 0;
|
|
1345
|
+
vm_var_sp = 0;
|
|
1346
|
+
vm_var_base = 0;
|
|
1347
|
+
vm_call_sp = 0;
|
|
1348
|
+
vm_running = 1;
|
|
1349
|
+
for (int i = 0; i < strcount; i++) vm_vars[i].type = VAL_NULL;
|
|
1350
|
+
|
|
1351
|
+
// Execute
|
|
1352
|
+
vm_exec(bytecode, bclen, strings, strcount);
|
|
1353
|
+
|
|
1354
|
+
// Cleanup
|
|
1355
|
+
cleanup_vm();
|
|
1356
|
+
free(src);
|
|
1357
|
+
return vm_has_error ? 1 : 0;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
static int build_file(const char* path) {
|
|
1361
|
+
FILE* f = fopen(path, "rb");
|
|
1362
|
+
if (!f) { fprintf(stderr, "Error: cannot open '%s'\n", path); return 1; }
|
|
1363
|
+
fseek(f, 0, SEEK_END);
|
|
1364
|
+
long len = ftell(f);
|
|
1365
|
+
if(len<0){fclose(f);fprintf(stderr,"Error: cannot determine file size\n");return 1;}
|
|
1366
|
+
fseek(f, 0, SEEK_SET);
|
|
1367
|
+
char* src = (char*)malloc(len + 1);
|
|
1368
|
+
if(!src){fclose(f);fprintf(stderr,"Error: out of memory\n");return 1;}
|
|
1369
|
+
size_t read=fread(src, 1, len, f);
|
|
1370
|
+
src[read] = 0;
|
|
1371
|
+
fclose(f);
|
|
1372
|
+
|
|
1373
|
+
// Reset compiler state
|
|
1374
|
+
func_count = 0;
|
|
1375
|
+
|
|
1376
|
+
// Lex
|
|
1377
|
+
Lx lx_state = {src, 0, (int)read, 1};
|
|
1378
|
+
tn = 0; tp = 0;
|
|
1379
|
+
do { tks[tn++] = lx(&lx_state); } while (tks[tn-1].type != T_EOF && tn < 8192);
|
|
1380
|
+
|
|
1381
|
+
// Compile
|
|
1382
|
+
bclen = 0;
|
|
1383
|
+
varcount = 0;
|
|
1384
|
+
strcount = 0;
|
|
1385
|
+
while (tp < tn && tks[tp].type != T_EOF) compile_stmt();
|
|
1386
|
+
bc(OP_HALT);
|
|
1387
|
+
|
|
1388
|
+
// Write bytecode
|
|
1389
|
+
char outpath[512];
|
|
1390
|
+
snprintf(outpath, sizeof(outpath), "%s.nbsc", path);
|
|
1391
|
+
FILE* out = fopen(outpath, "wb");
|
|
1392
|
+
if (!out) { fprintf(stderr, "Error: cannot write '%s'\n", outpath); free(src); return 1; }
|
|
1393
|
+
// Write header: magic, version, counts
|
|
1394
|
+
fwrite("NBS1", 1, 4, out);
|
|
1395
|
+
uint32_t ver=2;fwrite(&ver,4,1,out);
|
|
1396
|
+
uint32_t bc32=(uint32_t)bclen;fwrite(&bc32,4,1,out);
|
|
1397
|
+
uint32_t sc32=(uint32_t)strcount;fwrite(&sc32,4,1,out);
|
|
1398
|
+
uint32_t fc32=(uint32_t)func_count;fwrite(&fc32,4,1,out);
|
|
1399
|
+
// Write string table
|
|
1400
|
+
for(int i=0;i<strcount;i++){
|
|
1401
|
+
uint32_t slen=(uint32_t)strlen(strings[i]);
|
|
1402
|
+
fwrite(&slen,4,1,out);
|
|
1403
|
+
fwrite(strings[i],1,slen,out);
|
|
1404
|
+
}
|
|
1405
|
+
// Write function table
|
|
1406
|
+
for(int i=0;i<func_count;i++){
|
|
1407
|
+
uint32_t nlen=(uint32_t)strlen(func_table[i].name);
|
|
1408
|
+
fwrite(&nlen,4,1,out);
|
|
1409
|
+
fwrite(func_table[i].name,1,nlen,out);
|
|
1410
|
+
fwrite(&func_table[i].addr,4,1,out);
|
|
1411
|
+
fwrite(&func_table[i].param_count,4,1,out);
|
|
1412
|
+
for(int j=0;j<func_table[i].param_count;j++){
|
|
1413
|
+
uint32_t plen=(uint32_t)strlen(func_table[i].params[j]);
|
|
1414
|
+
fwrite(&plen,4,1,out);
|
|
1415
|
+
fwrite(func_table[i].params[j],1,plen,out);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
// Write bytecode
|
|
1419
|
+
fwrite(bytecode, 1, bclen, out);
|
|
1420
|
+
fclose(out);
|
|
1421
|
+
printf("Compiled %s -> %s (%d bytes, %d functions)\n", path, outpath, bclen, func_count);
|
|
1422
|
+
free(src);
|
|
1423
|
+
return 0;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// Syntax highlighting (simple ANSI terminal output)
|
|
1427
|
+
static int highlight_file(const char* path) {
|
|
1428
|
+
FILE* f = fopen(path, "rb");
|
|
1429
|
+
if (!f) { fprintf(stderr, "Error: cannot open '%s'\n", path); return 1; }
|
|
1430
|
+
fseek(f, 0, SEEK_END);
|
|
1431
|
+
long len = ftell(f);
|
|
1432
|
+
if(len<0){fclose(f);return 1;}
|
|
1433
|
+
fseek(f, 0, SEEK_SET);
|
|
1434
|
+
char* src = (char*)malloc(len + 1);
|
|
1435
|
+
if(!src){fclose(f);return 1;}
|
|
1436
|
+
size_t read=fread(src, 1, len, f);
|
|
1437
|
+
src[read] = 0;
|
|
1438
|
+
fclose(f);
|
|
1439
|
+
|
|
1440
|
+
Lx lx_state = {src, 0, (int)read, 1};
|
|
1441
|
+
int token_count = 0;
|
|
1442
|
+
Tk tokens[8192];
|
|
1443
|
+
do { tokens[token_count++] = lx(&lx_state); } while (tokens[token_count-1].type != T_EOF && token_count < 8192);
|
|
1444
|
+
|
|
1445
|
+
for(int i=0; i<token_count; i++){
|
|
1446
|
+
Tk* t = &tokens[i];
|
|
1447
|
+
switch(t->type){
|
|
1448
|
+
case T_INT: printf("\033[33m%lld\033[0m", t->ival); break;
|
|
1449
|
+
case T_STR: printf("\033[32m\"%s\"\033[0m", t->txt); break;
|
|
1450
|
+
case T_PRINT: case T_IF: case T_ELSE: case T_ELSEIF: case T_THEN:
|
|
1451
|
+
case T_WHILE: case T_FOR: case T_TO: case T_FUNC: case T_END:
|
|
1452
|
+
case T_RETURN: case T_LET: case T_BREAK: case T_CONTINUE:
|
|
1453
|
+
case T_AND: case T_OR: case T_NOT:
|
|
1454
|
+
case T_TRY: case T_CATCH: case T_THROW: case T_FINALLY:
|
|
1455
|
+
printf("\033[36m%s\033[0m", t->txt); break;
|
|
1456
|
+
case T_IDENT: printf("\033[37m%s\033[0m", t->txt); break;
|
|
1457
|
+
case T_PLUS: case T_MINUS: case T_STAR: case T_SLASH: case T_MOD:
|
|
1458
|
+
case T_EQ: case T_NEQ: case T_LT: case T_GT: case T_LTE: case T_GTE:
|
|
1459
|
+
case T_ASSIGN: case T_BITAND: case T_BITOR: case T_LSHIFT: case T_RSHIFT:
|
|
1460
|
+
printf("\033[35m%s\033[0m", t->txt); break;
|
|
1461
|
+
case T_LPAREN: case T_RPAREN: case T_LBRACKET: case T_RBRACKET:
|
|
1462
|
+
printf("\033[33m%s\033[0m", t->txt); break;
|
|
1463
|
+
case T_COMMA: case T_COLON:
|
|
1464
|
+
printf("\033[37m%s\033[0m", t->txt); break;
|
|
1465
|
+
case T_EOF: break;
|
|
1466
|
+
default: printf("%s", t->txt); break;
|
|
1467
|
+
}
|
|
1468
|
+
// Add space after tokens (except at line end)
|
|
1469
|
+
if(i < token_count-1 && tokens[i].type != T_EOF){
|
|
1470
|
+
if(tokens[i].line != tokens[i+1].line) printf("\n");
|
|
1471
|
+
else if(tokens[i+1].type != T_COMMA && tokens[i+1].type != T_COLON &&
|
|
1472
|
+
tokens[i].type != T_COMMA && tokens[i].type != T_COLON &&
|
|
1473
|
+
tokens[i].type != T_LPAREN && tokens[i+1].type != T_RPAREN &&
|
|
1474
|
+
tokens[i].type != T_LBRACKET && tokens[i+1].type != T_RBRACKET)
|
|
1475
|
+
printf(" ");
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
printf("\n");
|
|
1479
|
+
free(src);
|
|
1480
|
+
return 0;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
static void repl(void) {
|
|
1484
|
+
printf("Nebulara v2.0 > Type 'exit' to quit\n");
|
|
1485
|
+
char line[1024];
|
|
1486
|
+
// Initialize VM state for REPL
|
|
1487
|
+
memset(vm_vars, 0, sizeof(vm_vars));
|
|
1488
|
+
func_count = 0;
|
|
1489
|
+
while (1) {
|
|
1490
|
+
printf(">> ");
|
|
1491
|
+
fflush(stdout);
|
|
1492
|
+
if (!fgets(line, sizeof(line), stdin)) break;
|
|
1493
|
+
// Remove newline
|
|
1494
|
+
int len = (int)strlen(line);
|
|
1495
|
+
while (len > 0 && (line[len-1]=='\n'||line[len-1]=='\r')) line[--len]=0;
|
|
1496
|
+
if (len == 0) continue;
|
|
1497
|
+
if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) break;
|
|
1498
|
+
|
|
1499
|
+
// Lex + compile + run inline
|
|
1500
|
+
Lx lx_state = {line, 0, len, 1};
|
|
1501
|
+
tn = 0; tp = 0;
|
|
1502
|
+
do { tks[tn++] = lx(&lx_state); } while (tks[tn-1].type != T_EOF && tn < 8192);
|
|
1503
|
+
|
|
1504
|
+
bclen = 0;
|
|
1505
|
+
int saved_varcount = varcount;
|
|
1506
|
+
int saved_strcount = strcount;
|
|
1507
|
+
while (tp < tn && tks[tp].type != T_EOF) compile_stmt();
|
|
1508
|
+
bc(OP_HALT);
|
|
1509
|
+
|
|
1510
|
+
// Reset VM stack (but keep variables for REPL persistence)
|
|
1511
|
+
for (int i = 0; i < vm_sp; i++) val_free(vm_stack[i]);
|
|
1512
|
+
vm_sp = 0;
|
|
1513
|
+
vm_call_sp = 0;
|
|
1514
|
+
vm_running = 1;
|
|
1515
|
+
vm_has_error = 0;
|
|
1516
|
+
|
|
1517
|
+
vm_exec(bytecode, bclen, strings, strcount);
|
|
1518
|
+
|
|
1519
|
+
if(vm_has_error) fprintf(stderr, "Error: %s\n", vm_error_msg);
|
|
1520
|
+
}
|
|
1521
|
+
cleanup_vm();
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
int main(int argc, char** argv) {
|
|
1525
|
+
if (argc < 2) { print_version(); return 0; }
|
|
1526
|
+
|
|
1527
|
+
// Store global args for ARGUMENT_COUNT/ARGUMENT builtins
|
|
1528
|
+
g_argc = argc;
|
|
1529
|
+
g_argv = argv;
|
|
1530
|
+
|
|
1531
|
+
if (strcmp(argv[1], "run") == 0) {
|
|
1532
|
+
if (argc < 3) { fprintf(stderr, "Usage: nebulara run <file.nbs>\n"); return 1; }
|
|
1533
|
+
return run_file(argv[2]);
|
|
1534
|
+
} else if (strcmp(argv[1], "build") == 0) {
|
|
1535
|
+
if (argc < 3) { fprintf(stderr, "Usage: nebulara build <file.nbs>\n"); return 1; }
|
|
1536
|
+
return build_file(argv[2]);
|
|
1537
|
+
} else if (strcmp(argv[1], "highlight") == 0) {
|
|
1538
|
+
if (argc < 3) { fprintf(stderr, "Usage: nebulara highlight <file.nbs>\n"); return 1; }
|
|
1539
|
+
return highlight_file(argv[2]);
|
|
1540
|
+
} else if (strcmp(argv[1], "repl") == 0) {
|
|
1541
|
+
repl();
|
|
1542
|
+
return 0;
|
|
1543
|
+
} else if (strcmp(argv[1], "version") == 0) {
|
|
1544
|
+
fprintf(stderr, "Nebulara v2.0\n");
|
|
1545
|
+
return 0;
|
|
1546
|
+
} else if (strcmp(argv[1], "help") == 0) {
|
|
1547
|
+
print_version();
|
|
1548
|
+
return 0;
|
|
1549
|
+
} else {
|
|
1550
|
+
// If it's a file, just run it
|
|
1551
|
+
return run_file(argv[1]);
|
|
1552
|
+
}
|
|
1553
|
+
}
|