MIRROR: javascript for 🐜's, a tiny runtime with big ambitions
1/**
2 * Ant JavaScript Engine - Embedding Example
3 * This demonstrates how to embed the Ant JS engine in a C application.
4 *
5 * to build:
6 * ./libant/build.sh
7 * ./libant/example.sh
8 *
9 * to run:
10 * ./libant/dist/embed
11 */
12
13#include <ant.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18#define SEPARATOR "════════════════════════════════════════════════════════════"
19#define SUBSEP "────────────────────────────────────────────────────────────"
20
21static void print_header(int num, const char *title) {
22 printf("\n%s\n", SEPARATOR);
23 printf(" Example %d: %s\n", num, title);
24 printf("%s\n\n", SUBSEP);
25}
26
27static ant_t *create_js_runtime(void *stack_base) {
28 ant_t *js = js_create_dynamic();
29 if (!js) {
30 fprintf(stderr, "Failed to create JS runtime\n");
31 return NULL;
32 }
33
34 js_setstackbase(js, stack_base);
35
36 static char *default_argv[] = { "embed_example", NULL };
37 ant_runtime_init(js, 1, default_argv, NULL);
38
39 return js;
40}
41
42static void example_basic_eval(void) {
43 print_header(1, "Basic Evaluation");
44
45 volatile char stack_base;
46 ant_t *js = create_js_runtime((void *)&stack_base);
47 if (!js) return;
48
49
50
51 const char *code = "1 + 2 * 3";
52 ant_value_t result = js_eval_bytecode_eval(js, code, strlen(code));
53
54 if (vtype(result) == T_NUM) {
55 printf(" Result: %g\n", js_getnum(result));
56 } else if (vtype(result) == T_ERR) {
57 printf(" Error: %s\n", js_str(js, result));
58 }
59
60 js_destroy(js);
61}
62
63static ant_value_t my_add(ant_t *js, ant_value_t *args, int nargs) {
64 if (!js_chkargs(args, nargs, "dd")) {
65 return js_mkerr(js, "add() expects two numbers");
66 }
67
68 double a = js_getnum(args[0]);
69 double b = js_getnum(args[1]);
70
71 return js_mknum(a + b);
72}
73
74static ant_value_t my_greet(ant_t *js, ant_value_t *args, int nargs) {
75 if (nargs < 1 || vtype(args[0]) != T_STR) {
76 return js_mkerr(js, "greet() expects a string");
77 }
78
79 size_t len;
80 char *name = js_getstr(js, args[0], &len);
81
82 char buf[256];
83 snprintf(buf, sizeof(buf), "Hello, %s!", name);
84
85 return js_mkstr(js, buf, strlen(buf));
86}
87
88static ant_value_t my_create_point(ant_t *js, ant_value_t *args, int nargs) {
89 if (!js_chkargs(args, nargs, "dd")) {
90 return js_mkerr(js, "createPoint() expects two numbers");
91 }
92
93 ant_value_t obj = js_mkobj(js);
94 js_set(js, obj, "x", args[0]);
95 js_set(js, obj, "y", args[1]);
96
97 return obj;
98}
99
100static void example_c_functions(void) {
101 print_header(2, "C Functions");
102
103 volatile char stack_base;
104 ant_t *js = create_js_runtime((void *)&stack_base);
105 if (!js) return;
106
107
108
109 ant_value_t global = js_glob(js);
110 js_set(js, global, "add", js_mkfun(my_add));
111 js_set(js, global, "greet", js_mkfun(my_greet));
112 js_set(js, global, "createPoint", js_mkfun(my_create_point));
113
114 const char *code1 = "add(10, 32)";
115 ant_value_t r1 = js_eval_bytecode_eval(js, code1, strlen(code1));
116 printf(" add(10, 32) = %g\n", js_getnum(r1));
117
118 const char *code2 = "greet('World')";
119 ant_value_t r2 = js_eval_bytecode_eval(js, code2, strlen(code2));
120 printf(" greet('World') = %s\n", js_str(js, r2));
121
122 const char *code3 = "let p = createPoint(3, 4); p.x * p.x + p.y * p.y";
123 ant_value_t r3 = js_eval_bytecode_eval(js, code3, strlen(code3));
124 printf(" distance² = %g\n", js_getnum(r3));
125
126 js_destroy(js);
127}
128
129static void example_objects_arrays(void) {
130 print_header(3, "Objects and Arrays");
131
132 volatile char stack_base;
133 ant_t *js = create_js_runtime((void *)&stack_base);
134 if (!js) return;
135
136
137 ant_value_t global = js_glob(js);
138
139 ant_value_t config = js_mkobj(js);
140 js_set(js, config, "debug", js_true);
141 js_set(js, config, "version", js_mknum(1.0));
142 js_set(js, config, "name", js_mkstr(js, "MyApp", 5));
143 js_set(js, global, "config", config);
144
145 ant_value_t arr = js_mkarr(js);
146 js_arr_push(js, arr, js_mknum(10));
147 js_arr_push(js, arr, js_mknum(20));
148 js_arr_push(js, arr, js_mknum(30));
149 js_set(js, global, "numbers", arr);
150
151 const char *code = "config.name + ' v' + config.version + ' - sum: ' + numbers.reduce((a,b) => a+b, 0)";
152 ant_value_t result = js_eval_bytecode_eval(js, code, strlen(code));
153 printf(" Result: %s\n", js_str(js, result));
154
155 ant_value_t name_val = js_get(js, config, "name");
156 printf(" config.name: %s\n", js_str(js, name_val));
157
158 ant_value_t debug_val = js_get(js, config, "debug");
159 printf(" config.debug: %s\n", js_str(js, debug_val));
160
161 js_destroy(js);
162}
163
164static void example_error_handling(void) {
165 print_header(4, "Error Handling");
166
167 volatile char stack_base;
168 ant_t *js = create_js_runtime((void *)&stack_base);
169 if (!js) return;
170
171
172
173 const char *bad_code = "let x = {";
174 ant_value_t r1 = js_eval_bytecode_eval(js, bad_code, strlen(bad_code));
175 if (vtype(r1) == T_ERR) {
176 printf(" Syntax error: %s\n", js_str(js, r1));
177 }
178
179 const char *ref_err = "undefinedVariable + 1";
180 ant_value_t r2 = js_eval_bytecode_eval(js, ref_err, strlen(ref_err));
181 if (vtype(r2) == T_ERR) {
182 printf(" Reference error: %s\n", js_str(js, r2));
183 }
184
185 ant_value_t global = js_glob(js);
186 js_set(js, global, "add", js_mkfun(my_add));
187
188 const char *type_err = "add('not', 'numbers')";
189 ant_value_t r3 = js_eval_bytecode_eval(js, type_err, strlen(type_err));
190 if (vtype(r3) == T_ERR) {
191 printf(" Type error: %s\n", js_str(js, r3));
192 }
193
194 js_destroy(js);
195}
196
197static void example_call_js_from_c(void) {
198 print_header(5, "Calling JS from C");
199
200 volatile char stack_base;
201 ant_t *js = create_js_runtime((void *)&stack_base);
202 if (!js) return;
203
204 const char *code =
205 "function multiply(a, b) {"
206 " return a * b;"
207 "}"
208 "function formatName(first, last) {"
209 " return last + ', ' + first;"
210 "}";
211
212 js_eval_bytecode_eval(js, code, strlen(code));
213
214 ant_value_t glob = js_glob(js);
215 ant_value_t multiply_fn = js_get(js, glob, "multiply");
216 ant_value_t format_fn = js_get(js, glob, "formatName");
217
218 ant_value_t args1[] = { js_mknum(6), js_mknum(7) };
219 ant_value_t result1 = sv_vm_call(js->vm, js, multiply_fn, js_mkundef(), args1, 2, NULL, false);
220 printf(" multiply(6, 7) = %g\n", js_getnum(result1));
221
222 ant_value_t args2[] = {
223 js_mkstr(js, "John", 4),
224 js_mkstr(js, "Doe", 3)
225 };
226
227 ant_value_t result2 = sv_vm_call(js->vm, js, format_fn, js_mkundef(), args2, 2, NULL, false);
228 printf(" formatName('John', 'Doe') = %s\n", js_str(js, result2));
229
230 js_destroy(js);
231}
232
233static void example_iterate_properties(void) {
234 print_header(6, "Iterating Properties");
235
236 volatile char stack_base;
237 ant_t *js = create_js_runtime((void *)&stack_base);
238 if (!js) return;
239
240 const char *code = "({ name: 'Alice', age: 30, city: 'NYC' })";
241 ant_value_t obj = js_eval_bytecode_eval(js, code, strlen(code));
242
243 ant_iter_t iter = js_prop_iter_begin(js, obj);
244 const char *key;
245 size_t key_len;
246 ant_value_t value;
247
248 printf(" Object properties:\n");
249 while (js_prop_iter_next(&iter, &key, &key_len, &value)) {
250 printf(" • %.*s = %s\n", (int)key_len, key, js_str(js, value));
251 }
252 js_prop_iter_end(&iter);
253
254 js_destroy(js);
255}
256
257static ant_value_t method_get_full_name(ant_t *js, ant_value_t *args, int nargs) {
258 (void)args; (void)nargs;
259
260 ant_value_t this_obj = js_getthis(js);
261
262 ant_value_t first = js_get(js, this_obj, "firstName");
263 ant_value_t last = js_get(js, this_obj, "lastName");
264
265 size_t first_len, last_len;
266 char *first_str = js_getstr(js, first, &first_len);
267 char *last_str = js_getstr(js, last, &last_len);
268
269 char buf[256];
270 snprintf(buf, sizeof(buf), "%s %s", first_str, last_str);
271
272 return js_mkstr(js, buf, strlen(buf));
273}
274
275static void example_this_context(void) {
276 print_header(7, "'this' Context");
277
278 volatile char stack_base;
279 ant_t *js = create_js_runtime((void *)&stack_base);
280 if (!js) return;
281
282
283
284 ant_value_t person = js_mkobj(js);
285 js_set(js, person, "firstName", js_mkstr(js, "Jane", 4));
286 js_set(js, person, "lastName", js_mkstr(js, "Smith", 5));
287 js_set(js, person, "getFullName", js_mkfun(method_get_full_name));
288
289 js_set(js, js_glob(js), "person", person);
290
291 const char *code = "person.getFullName()";
292 ant_value_t result = js_eval_bytecode_eval(js, code, strlen(code));
293 printf(" person.getFullName() = %s\n", js_str(js, result));
294
295 js_destroy(js);
296}
297
298static void example_stateful_session(void) {
299 print_header(8, "Stateful Session");
300
301 volatile char stack_base;
302 ant_t *js = create_js_runtime((void *)&stack_base);
303 if (!js) return;
304
305
306
307 const char *scripts[] = {
308 "let counter = 0;",
309 "function increment() { return ++counter; }",
310 "function getCount() { return counter; }",
311 "increment(); increment(); increment();",
312 "getCount()"
313 };
314
315 ant_value_t result = js_mkundef();
316 for (int i = 0; i < 5; i++) {
317 result = js_eval_bytecode_eval(js, scripts[i], strlen(scripts[i]));
318 if (vtype(result) == T_ERR) {
319 printf(" Error in script %d: %s\n", i, js_str(js, result));
320 break;
321 }
322 }
323
324 printf(" Final count: %g\n", js_getnum(result));
325
326 js_destroy(js);
327}
328
329static void example_async_event_loop(void) {
330 print_header(9, "Async & Event Loop");
331
332 volatile char stack_base;
333 ant_t *js = create_js_runtime((void *)&stack_base);
334 if (!js) return;
335
336 init_symbol_module();
337 init_builtin_module();
338 init_timer_module();
339
340 const char *code =
341 "let results = [];"
342 ""
343 "setTimeout(() => {"
344 " results.push('timer 1 (50ms)');"
345 "}, 50);"
346 ""
347 "setTimeout(() => {"
348 " results.push('timer 2 (10ms)');"
349 "}, 10);"
350 ""
351 "Promise.resolve('promise 1').then(v => {"
352 " results.push(v);"
353 "});"
354 ""
355 "queueMicrotask(() => {"
356 " results.push('microtask');"
357 "});"
358 ""
359 "results.push('sync');";
360
361 ant_value_t result = js_eval_bytecode_eval(js, code, strlen(code));
362 if (vtype(result) == T_ERR) {
363 printf(" Error: %s\n", js_str(js, result));
364 js_destroy(js);
365 return;
366 }
367
368 js_run_event_loop(js);
369
370 ant_value_t results = js_get(js, js_glob(js), "results");
371
372 printf(" Execution order:\n");
373 ant_offset_t len = js_arr_len(js, results);
374 for (ant_offset_t i = 0; i < len; i++) {
375 ant_value_t item = js_arr_get(js, results, i);
376 printf(" %llu. %s\n", (unsigned long long)i + 1, js_str(js, item));
377 }
378
379 js_destroy(js);
380}
381
382static void example_console_logging(void) {
383 print_header(10, "Console Logging");
384
385 volatile char stack_base;
386 ant_t *js = create_js_runtime((void *)&stack_base);
387 if (!js) return;
388
389 init_console_module();
390
391
392 const char *code =
393 "console.log('Hello from JavaScript!');"
394 "console.log('Number:', 42, 'Boolean:', true);"
395 "console.log('Object:', { name: 'test', value: 123 });"
396 "console.log('Array:', [1, 2, 3]);"
397 "console.warn('This is a warning');"
398 "console.error('This is an error');"
399 "'done'";
400
401 ant_value_t result = js_eval_bytecode_eval(js, code, strlen(code));
402 if (vtype(result) == T_ERR) {
403 printf(" Error: %s\n", js_str(js, result));
404 }
405
406 js_destroy(js);
407}
408
409static void example_global_this(void) {
410 print_header(11, "GlobalThis");
411
412 volatile char stack_base;
413 ant_t *js = create_js_runtime((void *)&stack_base);
414 if (!js) return;
415
416 init_console_module();
417
418
419 ant_value_t global = js_glob(js);
420 js_set(js, global, "myNumber", js_mknum(42));
421 js_set(js, global, "myString", js_mkstr(js, "hello from C", 12));
422 js_set(js, global, "myBool", js_true);
423
424 ant_value_t myObj = js_mkobj(js);
425 js_set(js, myObj, "a", js_mknum(1));
426 js_set(js, myObj, "b", js_mknum(2));
427 js_set(js, global, "myObject", myObj);
428
429 const char *code =
430 "console.log('globalThis.myNumber:', globalThis.myNumber);"
431 "console.log('globalThis.myString:', globalThis.myString);"
432 "console.log('globalThis.myBool:', globalThis.myBool);"
433 "console.log('globalThis.myObject:', globalThis.myObject);"
434 ""
435 "globalThis.addedFromJS = 'I was added from JavaScript';"
436 "console.log('globalThis.addedFromJS:', globalThis.addedFromJS);"
437 ""
438 "console.log('\\nAll custom globals:');"
439 "console.log(' myNumber:', myNumber);"
440 "console.log(' myString:', myString);"
441 "console.log(' myBool:', myBool);"
442 "console.log(' myObject:', myObject);"
443 "console.log(' addedFromJS:', addedFromJS);"
444 "console.log(this)";
445
446 ant_value_t result = js_eval_bytecode_eval(js, code, strlen(code));
447 if (vtype(result) == T_ERR) {
448 printf(" Error: %s\n", js_str(js, result));
449 }
450
451 ant_value_t added = js_get(js, global, "addedFromJS");
452 printf("\n Read from C: addedFromJS = %s\n", js_str(js, added));
453
454 js_destroy(js);
455}
456
457int main(void) {
458 printf("\n%s\n", SEPARATOR);
459 printf(" Ant JavaScript Engine - Embedding Examples\n");
460 printf("%s\n", SEPARATOR);
461
462 example_basic_eval();
463 example_c_functions();
464 example_objects_arrays();
465 example_error_handling();
466 example_call_js_from_c();
467 example_iterate_properties();
468 example_this_context();
469 example_stateful_session();
470 example_async_event_loop();
471 example_console_logging();
472 example_global_this();
473
474 return EXIT_SUCCESS;
475}