nebulara 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/Compiler/nbs-bootstrap.c +2229 -0
  2. package/Compiler/nbs_cli.c +1553 -0
  3. package/Compiler/neb-cli.exe +0 -0
  4. package/Compiler/neb-codegen.c +287 -0
  5. package/Compiler/neb-ffi.c +303 -0
  6. package/Compiler/neb-ir.c +307 -0
  7. package/Compiler/neb-knowledge.c +203 -0
  8. package/Compiler/neb-pipeline.c +787 -0
  9. package/Compiler/neb-semantic.c +261 -0
  10. package/Compiler/nebulara.exe +0 -0
  11. package/README.md +258 -0
  12. package/SPEC.md +267 -0
  13. package/bin/neb.js +36 -0
  14. package/build/neb-cli.exe +0 -0
  15. package/build/neb-codegen.exe +0 -0
  16. package/build/neb-ffi.exe +0 -0
  17. package/build/neb-knowledge.exe +0 -0
  18. package/build/neb-pipeline.exe +0 -0
  19. package/build/nebulara.exe +0 -0
  20. package/dist/index.js +131 -0
  21. package/package.json +39 -0
  22. package/std/collections.nbs +72 -0
  23. package/std/json.nbs +15 -0
  24. package/std/kanban.nbs +122 -0
  25. package/std/math.nbs +52 -0
  26. package/std/net.nbs +22 -0
  27. package/std/primitives.nbs +30 -0
  28. package/std/string.nbs +71 -0
  29. package/std/time.nbs +15 -0
  30. package/test/broken.nbs +2 -0
  31. package/test/hello.nbs +6 -0
  32. package/test/test-arrays.nbs +22 -0
  33. package/test/test-charat.nbs +7 -0
  34. package/test/test-cont-debug.nbs +8 -0
  35. package/test/test-cont-min.nbs +7 -0
  36. package/test/test-cont-parens.nbs +7 -0
  37. package/test/test-cont2.nbs +7 -0
  38. package/test/test-cont3.nbs +7 -0
  39. package/test/test-control.nbs +18 -0
  40. package/test/test-functions.nbs +13 -0
  41. package/test/test-loops.nbs +17 -0
  42. package/test/test-mod-for.nbs +6 -0
  43. package/test/test-mod.nbs +5 -0
  44. package/test/test-new-features.nbs +39 -0
  45. package/test/test-recursion.nbs +23 -0
  46. package/test/test-runtime.nbs +8 -0
  47. package/test/test-strings.nbs +14 -0
  48. package/test/test-trycatch.nbs +11 -0
Binary file
@@ -0,0 +1,287 @@
1
+ /*
2
+ * neb-codegen.c - Native x86/x64 Code Generation for Nebulara
3
+ *
4
+ * Generates native machine code from IR instructions.
5
+ * Supports x86-32 (i686) and x64 (x86-64) targets.
6
+ *
7
+ * Build: gcc -o neb-codegen.exe neb-codegen.c -static -O2
8
+ */
9
+
10
+ #include <stdio.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+
14
+ /* Codegen targets */
15
+ typedef enum {
16
+ TARGET_X86_32,
17
+ TARGET_X64,
18
+ TARGET_ARM64
19
+ } CodegenTarget;
20
+
21
+ /* x86 Registers */
22
+ typedef enum {
23
+ /* 32-bit */
24
+ EAX = 0, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
25
+ /* 64-bit */
26
+ RAX = 0, RCX, RDX, RBX, RSP, RBP, RSI, RDI,
27
+ R8, R9, R10, R11, R12, R13, R14, R15
28
+ } Register;
29
+
30
+ /* Register allocation */
31
+ typedef struct {
32
+ int reg[16]; /* 1=used, 0=free */
33
+ int spill_count;
34
+ CodegenTarget target;
35
+ } RegAlloc;
36
+
37
+ /* Code buffer */
38
+ typedef struct {
39
+ unsigned char *code;
40
+ int size;
41
+ int capacity;
42
+ int labels[1024];
43
+ int label_targets[1024];
44
+ int label_count;
45
+ } CodeBuffer;
46
+
47
+ static CodeBuffer code_buf;
48
+ static RegAlloc reg_alloc;
49
+ static CodegenTarget current_target = TARGET_X64;
50
+
51
+ /* Code buffer management */
52
+ void code_init(void) {
53
+ code_buf.capacity = 65536;
54
+ code_buf.code = (unsigned char *)malloc(code_buf.capacity);
55
+ code_buf.size = 0;
56
+ code_buf.label_count = 0;
57
+ memset(&reg_alloc, 0, sizeof(RegAlloc));
58
+ reg_alloc.target = current_target;
59
+ }
60
+
61
+ void code_emit(unsigned char byte) {
62
+ if (code_buf.size >= code_buf.capacity) {
63
+ code_buf.capacity *= 2;
64
+ code_buf.code = (unsigned char *)realloc(code_buf.code, code_buf.capacity);
65
+ }
66
+ code_buf.code[code_buf.size++] = byte;
67
+ }
68
+
69
+ void code_emit32(unsigned int val) {
70
+ code_emit(val & 0xFF);
71
+ code_emit((val >> 8) & 0xFF);
72
+ code_emit((val >> 16) & 0xFF);
73
+ code_emit((val >> 24) & 0xFF);
74
+ }
75
+
76
+ void code_emit64(unsigned long long val) {
77
+ code_emit32(val & 0xFFFFFFFF);
78
+ code_emit32((val >> 32) & 0xFFFFFFFF);
79
+ }
80
+
81
+ /* Register management */
82
+ int reg_alloc_alloc(void) {
83
+ for (int i = 0; i < 16; i++) {
84
+ if (!reg_alloc.reg[i]) {
85
+ reg_alloc.reg[i] = 1;
86
+ return i;
87
+ }
88
+ }
89
+ /* All registers used - spill */
90
+ reg_alloc.spill_count++;
91
+ return -1;
92
+ }
93
+
94
+ void reg_free(int reg) {
95
+ if (reg >= 0 && reg < 16) {
96
+ reg_alloc.reg[reg] = 0;
97
+ }
98
+ }
99
+
100
+ /* x86/x64 instruction encoding */
101
+ void emit_mov_reg_imm(int reg, unsigned long long imm) {
102
+ if (current_target == TARGET_X64) {
103
+ /* REX.W prefix for 64-bit */
104
+ if (reg >= 8) {
105
+ code_emit(0x49);
106
+ } else {
107
+ code_emit(0x48);
108
+ }
109
+ code_emit(0xB8 + (reg & 7));
110
+ code_emit64(imm);
111
+ } else {
112
+ /* 32-bit: MOV r32, imm32 */
113
+ code_emit(0xB8 + reg);
114
+ code_emit32((unsigned int)imm);
115
+ }
116
+ }
117
+
118
+ void emit_mov_reg_reg(int dst, int src) {
119
+ if (current_target == TARGET_X64) {
120
+ code_emit(0x48); /* REX.W */
121
+ }
122
+ code_emit(0x89);
123
+ code_emit(0xC0 + (((src & 7) << 3) | (dst & 7)));
124
+ }
125
+
126
+ void emit_add_reg_reg(int dst, int src) {
127
+ if (current_target == TARGET_X64) {
128
+ code_emit(0x48); /* REX.W */
129
+ }
130
+ code_emit(0x01);
131
+ code_emit(0xC0 + (((src & 7) << 3) | (dst & 7)));
132
+ }
133
+
134
+ void emit_sub_reg_reg(int dst, int src) {
135
+ if (current_target == TARGET_X64) {
136
+ code_emit(0x48); /* REX.W */
137
+ }
138
+ code_emit(0x29);
139
+ code_emit(0xC0 + (((src & 7) << 3) | (dst & 7)));
140
+ }
141
+
142
+ void emit_imul_reg_reg(int dst, int src) {
143
+ if (current_target == TARGET_X64) {
144
+ code_emit(0x48); /* REX.W */
145
+ }
146
+ code_emit(0x0F);
147
+ code_emit(0xAF);
148
+ code_emit(0xC0 + (((dst & 7) << 3) | (src & 7)));
149
+ }
150
+
151
+ void emit_cmp_reg_reg(int reg1, int reg2) {
152
+ if (current_target == TARGET_X64) {
153
+ code_emit(0x48); /* REX.W */
154
+ }
155
+ code_emit(0x39);
156
+ code_emit(0xC0 + (((reg2 & 7) << 3) | (reg1 & 7)));
157
+ }
158
+
159
+ void emit_jcc(int condition, int offset) {
160
+ code_emit(0x0F);
161
+ code_emit(0x80 + condition);
162
+ code_emit32(offset);
163
+ }
164
+
165
+ /* Syscall for Linux/macOS */
166
+ void emit_syscall_exit(int exit_code) {
167
+ /* mov rax, 60 (sys_exit) */
168
+ emit_mov_reg_imm(RAX, 60);
169
+ /* mov rdi, exit_code */
170
+ emit_mov_reg_imm(RDI, exit_code);
171
+ /* syscall */
172
+ code_emit(0x0F);
173
+ code_emit(0x05);
174
+ }
175
+
176
+ /* Windows x64 calling convention stub */
177
+ void emit_win64_call_stub(void) {
178
+ /* sub rsp, 40 (shadow space) */
179
+ if (current_target == TARGET_X64) {
180
+ code_emit(0x48);
181
+ code_emit(0x83);
182
+ code_emit(0xEC);
183
+ code_emit(0x28);
184
+ }
185
+ }
186
+
187
+ /* Labels */
188
+ int codegen_new_label(void) {
189
+ return code_buf.label_count++;
190
+ }
191
+
192
+ void codegen_label(int label) {
193
+ code_buf.label_targets[label] = code_buf.size;
194
+ }
195
+
196
+ /* Save code to file */
197
+ int codegen_save(const char *filename) {
198
+ FILE *f = fopen(filename, "wb");
199
+ if (!f) return -1;
200
+
201
+ /* ELF64 header */
202
+ unsigned char elf_header[64] = {
203
+ 0x7F, 'E', 'L', 'F', /* magic */
204
+ 2, /* 64-bit */
205
+ 1, /* little-endian */
206
+ 1, /* ELF version */
207
+ 0, 0, 0, 0, 0, 0, 0, 0, /* padding */
208
+ 2, 0, /* ET_EXEC */
209
+ 0x3E, 0, /* x86-64 */
210
+ 1, 0, 0, 0, /* ELF version */
211
+ 0, 0, 0, 0, /* entry point (set below) */
212
+ 64, 0, 0, 0, /* phoff */
213
+ 0, 0, 0, 0, /* shoff */
214
+ 0, 0, 0, 0, /* flags */
215
+ 64, 0, /* ehsize */
216
+ 56, 0, /* phentsize */
217
+ 1, 0, /* phnum */
218
+ 64, 0, /* shentsize */
219
+ 0, 0, /* shnum */
220
+ 0, 0 /* shstrndx */
221
+ };
222
+
223
+ /* Set entry point to 0x400000 + 64 (ELF header) + 56 (program header) */
224
+ unsigned int entry = 0x400000 + 64 + 56;
225
+ memcpy(&elf_header[24], &entry, 4);
226
+
227
+ /* Program header */
228
+ unsigned char prog_header[56] = {0};
229
+ prog_header[0] = 1; /* PT_LOAD */
230
+ prog_header[4] = 5; /* PF_R | PF_X */
231
+ memcpy(&prog_header[8], &entry, 4); /* p_offset */
232
+ unsigned int vaddr = 0x400000;
233
+ memcpy(&prog_header[12], &vaddr, 4); /* p_vaddr */
234
+ memcpy(&prog_header[20], &vaddr, 4); /* p_paddr */
235
+ unsigned int filesz = 56 + code_buf.size;
236
+ unsigned int memsz = filesz;
237
+ memcpy(&prog_header[24], &filesz, 4);
238
+ memcpy(&prog_header[28], &memsz, 4);
239
+ prog_header[32] = 0x00; /* align low byte */
240
+ prog_header[33] = 0x10; /* align = 0x1000 */
241
+
242
+ fwrite(elf_header, 1, 64, f);
243
+ fwrite(prog_header, 1, 56, f);
244
+ fwrite(code_buf.code, 1, code_buf.size, f);
245
+ fclose(f);
246
+ return 0;
247
+ }
248
+
249
+ /* Print code as hex dump */
250
+ void codegen_dump_hex(void) {
251
+ printf("=== Native Code (%d bytes) ===\n", code_buf.size);
252
+ for (int i = 0; i < code_buf.size; i++) {
253
+ if (i % 16 == 0) printf("%04X: ", i);
254
+ printf("%02X ", code_buf.code[i]);
255
+ if (i % 16 == 15) printf("\n");
256
+ }
257
+ if (code_buf.size % 16 != 0) printf("\n");
258
+ printf("=== End Native Code ===\n");
259
+ }
260
+
261
+ void codegen_free(void) {
262
+ free(code_buf.code);
263
+ code_buf.code = NULL;
264
+ }
265
+
266
+ /* Demo: generate simple function */
267
+ int main(int argc, char *argv[]) {
268
+ printf("=== Nebulara Native Codegen Module ===\n\n");
269
+ printf("Target: x86-64\n\n");
270
+
271
+ current_target = TARGET_X64;
272
+ code_init();
273
+
274
+ /* Generate: mov rax, 42; ret */
275
+ int rax = reg_alloc_alloc();
276
+ emit_mov_reg_imm(rax, 42);
277
+ reg_free(rax);
278
+
279
+ /* ret instruction */
280
+ code_emit(0xC3);
281
+
282
+ codegen_dump_hex();
283
+ codegen_free();
284
+
285
+ printf("\nNative codegen module: OK\n");
286
+ return 0;
287
+ }
@@ -0,0 +1,303 @@
1
+ /*
2
+ * neb-ffi.c - Foreign Function Interface Bridge
3
+ *
4
+ * Allows Nebulara to call functions from C/C++ shared libraries.
5
+ * Uses dlopen on Linux/macOS and LoadLibrary on Windows.
6
+ *
7
+ * Build: gcc -o neb-ffi.exe neb-ffi.c -static -O2
8
+ */
9
+
10
+ #include <stdio.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+ #include <stdint.h>
14
+
15
+ #ifdef _WIN32
16
+ #include <windows.h>
17
+ typedef HMODULE FFIHandle;
18
+ #else
19
+ #include <dlfcn.h>
20
+ typedef void* FFIHandle;
21
+ #endif
22
+
23
+ /* FFI types */
24
+ typedef enum {
25
+ FFI_TYPE_VOID,
26
+ FFI_TYPE_INT,
27
+ FFI_TYPE_FLOAT,
28
+ FFI_TYPE_DOUBLE,
29
+ FFI_TYPE_STRING,
30
+ FFI_TYPE_POINTER,
31
+ FFI_TYPE_BOOL
32
+ } FFIType;
33
+
34
+ typedef struct {
35
+ char name[256];
36
+ FFIType return_type;
37
+ FFIType param_types[16];
38
+ int param_count;
39
+ FFIHandle handle;
40
+ void *fn_ptr;
41
+ } FFIFunction;
42
+
43
+ typedef struct {
44
+ char name[256];
45
+ char path[512];
46
+ FFIFunction functions[256];
47
+ int func_count;
48
+ int is_loaded;
49
+ } FFILibrary;
50
+
51
+ /* Global state */
52
+ static FFILibrary libraries[64];
53
+ static int library_count = 0;
54
+
55
+ /* Load a native library */
56
+ int ffi_load(const char *name, const char *path) {
57
+ if (library_count >= 64) return -1;
58
+
59
+ FFILibrary *lib = &libraries[library_count];
60
+ memset(lib, 0, sizeof(FFILibrary));
61
+ strncpy(lib->name, name, 255);
62
+ strncpy(lib->path, path, 511);
63
+ lib->func_count = 0;
64
+
65
+ #ifdef _WIN32
66
+ lib->is_loaded = 1;
67
+ /* LoadLibrary will be done on first ffi_call */
68
+ #else
69
+ /* Try loading immediately on Linux/macOS */
70
+ void *handle = dlopen(path, RTLD_LAZY);
71
+ if (handle) {
72
+ lib->is_loaded = 1;
73
+ } else {
74
+ lib->is_loaded = 0;
75
+ fprintf(stderr, "FFI WARNING: could not load '%s': %s\n", path, dlerror());
76
+ }
77
+ #endif
78
+
79
+ library_count++;
80
+ printf("FFI: Registered library '%s' from '%s'\n", name, path);
81
+ return 0;
82
+ }
83
+
84
+ /* Register a function from a library */
85
+ int ffi_register_func(const char *lib_name, const char *func_name,
86
+ FFIType ret_type, FFIType *param_types, int param_count) {
87
+ for (int i = 0; i < library_count; i++) {
88
+ if (strcmp(libraries[i].name, lib_name) == 0) {
89
+ FFILibrary *lib = &libraries[i];
90
+ if (lib->func_count >= 256) return -2;
91
+
92
+ FFIFunction *func = &lib->functions[lib->func_count];
93
+ memset(func, 0, sizeof(FFIFunction));
94
+ strncpy(func->name, func_name, 255);
95
+ func->return_type = ret_type;
96
+ func->param_count = param_count;
97
+ for (int j = 0; j < param_count && j < 16; j++) {
98
+ func->param_types[j] = param_types[j];
99
+ }
100
+ lib->func_count++;
101
+ return 0;
102
+ }
103
+ }
104
+ return -1;
105
+ }
106
+
107
+ /* Resolve a function pointer from a library handle */
108
+ static void *ffi_resolve_func(FFILibrary *lib, FFIFunction *func) {
109
+ if (func->fn_ptr) return func->fn_ptr;
110
+
111
+ #ifdef _WIN32
112
+ HMODULE h = LoadLibraryA(lib->path);
113
+ if (!h) {
114
+ fprintf(stderr, "FFI ERROR: Cannot load '%s': error %lu\n", lib->path, GetLastError());
115
+ return NULL;
116
+ }
117
+ func->fn_ptr = (void*)GetProcAddress(h, func->name);
118
+ #else
119
+ void *handle = dlopen(lib->path, RTLD_LAZY);
120
+ if (!handle) {
121
+ fprintf(stderr, "FFI ERROR: Cannot load '%s': %s\n", lib->path, dlerror());
122
+ return NULL;
123
+ }
124
+ func->fn_ptr = dlsym(handle, func->name);
125
+ #endif
126
+
127
+ if (!func->fn_ptr) {
128
+ fprintf(stderr, "FFI ERROR: Symbol '%s' not found in '%s'\n", func->name, lib->path);
129
+ }
130
+ return func->fn_ptr;
131
+ }
132
+
133
+ /* Call a foreign function */
134
+ int ffi_call(const char *lib_name, const char *func_name,
135
+ void **args, int arg_count, void *result) {
136
+ for (int i = 0; i < library_count; i++) {
137
+ if (strcmp(libraries[i].name, lib_name) == 0) {
138
+ for (int j = 0; j < libraries[i].func_count; j++) {
139
+ if (strcmp(libraries[i].functions[j].name, func_name) == 0) {
140
+ FFIFunction *func = &libraries[i].functions[j];
141
+ if (arg_count != func->param_count) {
142
+ fprintf(stderr, "FFI ERROR: %s.%s expects %d args, got %d\n",
143
+ lib_name, func_name, func->param_count, arg_count);
144
+ return -3;
145
+ }
146
+
147
+ void *fn = ffi_resolve_func(&libraries[i], func);
148
+ if (!fn) return -4;
149
+
150
+ /* Extract actual values based on param types.
151
+ * args[i] is a pointer TO the value; we dereference to get the real arg. */
152
+ intptr_t vals[16];
153
+ for (int k = 0; k < arg_count && k < 16; k++) {
154
+ switch (func->param_types[k]) {
155
+ case FFI_TYPE_INT:
156
+ case FFI_TYPE_BOOL:
157
+ vals[k] = (intptr_t)(*(int*)args[k]);
158
+ break;
159
+ case FFI_TYPE_FLOAT:
160
+ vals[k] = (intptr_t)(*(int*)args[k]); /* float passed as int bits on 32-bit */
161
+ break;
162
+ case FFI_TYPE_DOUBLE:
163
+ /* Doubles need special handling - pass pointer */
164
+ vals[k] = (intptr_t)args[k];
165
+ break;
166
+ default: /* STRING, POINTER, VOID */
167
+ vals[k] = (intptr_t)args[k];
168
+ break;
169
+ }
170
+ }
171
+
172
+ typedef intptr_t (*ffi_fn)();
173
+ ffi_fn call = (ffi_fn)fn;
174
+
175
+ switch (func->return_type) {
176
+ case FFI_TYPE_INT:
177
+ case FFI_TYPE_BOOL:
178
+ *(int*)result = (int)call(
179
+ arg_count > 0 ? vals[0] : 0,
180
+ arg_count > 1 ? vals[1] : 0,
181
+ arg_count > 2 ? vals[2] : 0,
182
+ arg_count > 3 ? vals[3] : 0,
183
+ arg_count > 4 ? vals[4] : 0
184
+ );
185
+ break;
186
+ case FFI_TYPE_STRING:
187
+ case FFI_TYPE_POINTER:
188
+ *(void**)result = (void*)call(
189
+ arg_count > 0 ? vals[0] : 0,
190
+ arg_count > 1 ? vals[1] : 0,
191
+ arg_count > 2 ? vals[2] : 0,
192
+ arg_count > 3 ? vals[3] : 0,
193
+ arg_count > 4 ? vals[4] : 0
194
+ );
195
+ break;
196
+ case FFI_TYPE_DOUBLE:
197
+ *(double*)result = (double)call(
198
+ arg_count > 0 ? vals[0] : 0,
199
+ arg_count > 1 ? vals[1] : 0,
200
+ arg_count > 2 ? vals[2] : 0,
201
+ arg_count > 3 ? vals[3] : 0,
202
+ arg_count > 4 ? vals[4] : 0
203
+ );
204
+ break;
205
+ default:
206
+ call(
207
+ arg_count > 0 ? vals[0] : 0,
208
+ arg_count > 1 ? vals[1] : 0,
209
+ arg_count > 2 ? vals[2] : 0,
210
+ arg_count > 3 ? vals[3] : 0,
211
+ arg_count > 4 ? vals[4] : 0
212
+ );
213
+ break;
214
+ }
215
+ return 0;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ return -1;
221
+ }
222
+
223
+ /* Type name helper */
224
+ const char *ffi_type_name(FFIType t) {
225
+ switch (t) {
226
+ case FFI_TYPE_VOID: return "void";
227
+ case FFI_TYPE_INT: return "int";
228
+ case FFI_TYPE_FLOAT: return "float";
229
+ case FFI_TYPE_DOUBLE: return "double";
230
+ case FFI_TYPE_STRING: return "string";
231
+ case FFI_TYPE_POINTER: return "pointer";
232
+ case FFI_TYPE_BOOL: return "bool";
233
+ default: return "unknown";
234
+ }
235
+ }
236
+
237
+ /* Print library info */
238
+ void ffi_info(void) {
239
+ printf("=== FFI Libraries (%d loaded) ===\n", library_count);
240
+ for (int i = 0; i < library_count; i++) {
241
+ printf(" %s (%s) - %d functions [%s]\n",
242
+ libraries[i].name, libraries[i].path, libraries[i].func_count,
243
+ libraries[i].is_loaded ? "loaded" : "not loaded");
244
+ for (int j = 0; j < libraries[i].func_count; j++) {
245
+ FFIFunction *f = &libraries[i].functions[j];
246
+ printf(" %s(", f->name);
247
+ for (int k = 0; k < f->param_count; k++) {
248
+ if (k > 0) printf(", ");
249
+ printf("%s", ffi_type_name(f->param_types[k]));
250
+ }
251
+ printf(") -> %s\n", ffi_type_name(f->return_type));
252
+ }
253
+ }
254
+ printf("=== End FFI ===\n");
255
+ }
256
+
257
+ /* Demo/test */
258
+ int main(int argc, char *argv[]) {
259
+ printf("=== Nebulara FFI Bridge Module ===\n\n");
260
+
261
+ /* Register and load system libraries */
262
+ #ifdef _WIN32
263
+ ffi_load("kernel32", "kernel32.dll");
264
+ ffi_load("msvcrt", "msvcrt.dll");
265
+
266
+ FFIType int_params[] = { FFI_TYPE_INT };
267
+ ffi_register_func("msvcrt", "abs", FFI_TYPE_INT, int_params, 1);
268
+
269
+ ffi_info();
270
+
271
+ /* Test: call msvcrt.abs(-42) */
272
+ int arg = -42;
273
+ void *args[] = { &arg };
274
+ int result = 0;
275
+ int rc = ffi_call("msvcrt", "abs", args, 1, &result);
276
+ if (rc == 0) {
277
+ printf("\nFFI TEST: msvcrt.abs(-42) = %d\n", result);
278
+ } else {
279
+ printf("\nFFI TEST: msvcrt.abs(-42) FAILED (rc=%d)\n", rc);
280
+ }
281
+ #else
282
+ ffi_load("libc", "libc.so.6");
283
+
284
+ FFIType string_params[] = { FFI_TYPE_STRING };
285
+ ffi_register_func("libc", "puts", FFI_TYPE_INT, string_params, 1);
286
+
287
+ ffi_info();
288
+
289
+ /* Test: call libc.puts("Hello from FFI!") */
290
+ const char *msg = "Hello from FFI!";
291
+ void *args[] = { (void*)msg };
292
+ int result = 0;
293
+ int rc = ffi_call("libc", "puts", args, 1, &result);
294
+ if (rc == 0) {
295
+ printf("FFI TEST: libc.puts() returned %d\n", result);
296
+ } else {
297
+ printf("FFI TEST: libc.puts() FAILED (rc=%d)\n", rc);
298
+ }
299
+ #endif
300
+
301
+ printf("\nFFI module: functional (dlopen/LoadLibrary)\n");
302
+ return 0;
303
+ }