@@ -76,17 +76,19 @@ This demonstrates how JS code can import and call existing C functions:
76
76
#include "elk.h"
77
77
78
78
// C function that adds two numbers. Will be called from JS
79
- int sum(int a, int b) {
80
- return a + b;
79
+ jsval_t sum(struct js *js, jsval_t *args, int nargs) {
80
+ if (nargs != 2) return js_err(js, "2 args expected");
81
+ double a = js_getnum(args[0]); // Fetch 1st arg
82
+ double b = js_getnum(args[1]); // Fetch 2nd arg
83
+ return js_mknum(a + b);
81
84
}
82
85
83
86
int main(void) {
84
87
char mem[200];
85
- struct js *js = js_create(mem, sizeof(mem)); // Create JS instance
86
- jsval_t v = js_import(js, sum, "iii"); // Import C function "sum"
87
- js_set(js, js_glob(js), "f", v); // Under the name "f"
88
- jsval_t result = js_eval(js, "f(3, 4);", ~0); // Call "f"
89
- printf("result: %s\n", js_str(js, result)); // result: 7
88
+ struct js *js = js_create(mem, sizeof(mem)); // Create JS instance
89
+ js_set(js, js_glob(js), "sum", js_mkfun(sum))); // Import sum()
90
+ jsval_t result = js_eval(js, "sum(3, 4);", ~0); // Call sum
91
+ printf("result: %s\n", js_str(js, result)); // result: 7
90
92
return 0;
91
93
}
92
94
```
@@ -146,6 +148,11 @@ $ xtensa-esp32-elf-gcc $CFLAGS elk.c -c elk.tmp
146
148
$ xtensa-esp32-elf-objcopy --rename-section .text=.irom0.text elk.tmp elk.o
147
149
```
148
150
151
+ Note: Elk uses ` snprintf() ` standard function to format numbers (double).
152
+ On some architectures, for example AVR Arduino, that standard function does
153
+ not support float formatting - therefore printing numbers may output nothing
154
+ or ` ? ` symbols.
155
+
149
156
## API reference
150
157
151
158
### js\_ create()
@@ -199,122 +206,76 @@ The string is allocated in the "free" memory section. If there is no
199
206
enough space there, an empty string is returned. The returned pointer
200
207
is valid until the next `js_eval()` call.
201
208
202
-
203
- ### js\_import()
209
+ ### js\_glob()
204
210
205
211
```c
206
- jsval_t js_import (struct js *js, uintptr_t funcaddr, const char *signature );
212
+ jsval_t js_glob (struct js *);
207
213
```
208
214
209
- Import an existing C function with address ` funcaddr ` and signature ` signature ` .
210
- Return imported function, suitable for subsequent ` js_set() ` .
211
-
212
- - ` js ` : JS instance
213
- - ` funcaddr ` : C function address: ` (uintptr_t) &my_function `
214
- - ` signature ` : specifies C function signature that tells how JS engine
215
- should marshal JS arguments to the C function.
216
- First letter specifies return value type, following letters - parameters:
217
- - ` b ` : a C ` bool ` type
218
- - ` d ` : a C ` double ` type
219
- - ` i ` : a C integer type: ` char ` , ` short ` , ` int ` , ` long `
220
- - ` s ` : a C string, a 0-terminated ` char * `
221
- - ` j ` : a ` jsval_t `
222
- - ` m ` : a current ` struct js * ` . In JS, pass ` null `
223
- - ` p ` : any C pointer
224
- - ` v ` : valid only for the return value, means ` void `
225
-
226
- The imported C function must satisfy the following requirements:
227
-
228
- - A function must have maximum 6 parameters
229
- - C ` double ` parameters could be only 1st or 2nd. For example, function
230
- ` void foo(double x, double y, struct bar *) ` could be imported, but
231
- ` void foo(struct bar *, double x, double y) ` could not
232
- - C++ functions must be declared as ` extern "C" `
233
- - Functions with ` float ` params cannot be imported. Write wrappers with ` double `
234
-
235
- Here are some example of the import specifications:
236
- - ` int sum(int) ` -> ` js_import(js, (uintptr_t) sum, "ii") `
237
- - ` double sub(double a, double b) ` -> ` js_import(js, (uintptr_t) sub, "ddd") `
238
- - ` int rand(void) ` -> ` js_import(js, (uintptr_t) rand, "i") `
239
- - ` unsigned long strlen(char *s) ` -> ` js_import(js, (uintptr_t) strlen, "is") `
240
- - ` char *js_str(struct js *, js_val_t) ` -> ` js_import(js, (uintptr_t) js_str, "smj") `
241
-
242
- In some cases, C APIs use callback functions. For example, a timer C API could
243
- specify a time interval, a C function to call, and a function parameter. It is
244
- possible to marshal JS function as a C callback - in other words, it is
245
- possible to pass JS functions as C callbacks.
246
-
247
- A C callback function should take between 1 and 6 arguments. One of these
248
- arguments must be a ` void * ` pointer, that is passed to the C callback by the
249
- imported function. We call this ` void * ` parameter a "userdata" parameter.
250
-
251
- The C callback specification is enclosed into the square brackets ` [...] ` .
252
- In addition to the signature letters above, a new letter ` u ` is available
253
- that specifies userdata parameter. In JS, pass ` null ` for ` u ` param.
254
- Here is a complete example:
215
+ Return global JS object, i.e. root namespace.
216
+
217
+ ### js\_ set()
255
218
256
219
``` c
257
- # include < stdio.h >
258
- # include " elk.h "
220
+ void js_set (struct js * , jsval_t obj, const char * key, jsval_t val);
221
+ ```
259
222
260
- // C function that invokes a callback and returns the result of invocation
261
- int f (int (* fn)(int a, int b, void * userdata), void * userdata) {
262
- return fn(1, 2, userdata);
263
- }
223
+ Assign an attribute `key` in in object `obj` to value `val`.
264
224
265
- int main(void) {
266
- char mem[ 500] ;
267
- struct js * js = js_create(mem, sizeof(mem));
268
- js_set(js, js_glob(js), "f", js_import(js, f, "i[ iiiu] u"));
269
- jsval_t v = js_eval(js, "f(function(a,b,c){return a + b;}, 0);", ~ 0);
270
- printf("result: %s\n", js_str(js, v)); // result: 3
271
- return 0;
272
- }
273
- ```
274
225
275
- ### js\_set(), js\_glob(), js\_mkobj(), js\_mknum(), js\_mkstr ()
226
+ ### js\_mk\* ()
276
227
277
228
```c
278
- jsval_t js_glob(struct js *); // Return global object
279
- jsval_t js_mkobj(struct js *); // Create a new object
280
- jsval_t js_mkstr(struct js *, const void *, size_t); // Create a string
281
- jsval_t js_mknum(struct js *, double); // Create a number
282
- void js_set(struct js *, jsval_t obj, const char *key, jsval_t val); // Assign property to an object
229
+ jsval_t js_mkval(int type); // Create undef, null, true, false
230
+ jsval_t js_mkobj(struct js *); // Create object
231
+ jsval_t js_mkstr(struct js *, const void *, size_t); // Create string
232
+ jsval_t js_mknum(double); // Create number
233
+ jsval_t js_mkerr(struct js *js, const char *fmt, ...); // Create error
234
+ jsval_t js_mkfun(jsval_t (*fn)(struct js *, int)); // Create func
283
235
```
284
236
285
- These are helper functions for assigning properties to objects. The
286
- anticipated use case is to give names to imported C functions.
237
+ Create JS values
238
+
239
+ ### js\_ get\* ()
287
240
288
- Importing a C function ` sum ` into the global namespace:
241
+ ### js \_ type()
289
242
290
243
``` c
291
- jsval_t global_namespace = js_glob(js);
292
- jsval_t imported_function = js_import(js, (uintptr_t ) sum, " iii" );
293
- js_set (js, global_namespace, "f", imported_function);
244
+ enum { JS_UNDEF, JS_NULL, JS_TRUE, JS_FALSE, JS_STR, JS_NUM, JS_ERR, JS_PRIV };
245
+ int js_type(jsval_t val); // Return JS value type
294
246
```
295
247
296
- Use `js_mkobj()` to create a dedicated object to hold groups of functions
297
- and keep a global namespace tidy. For example, all GPIO related functions
298
- can go into the `gpio` object:
248
+ Return type of the JS value.
299
249
300
- ```c
301
- jsval_t gpio = js_mkobj(js); // Equivalent to:
302
- js_set(js, js_glob(js), "gpio", gpio); // let gpio = {};
250
+ ## js\_checkargs()
303
251
304
- js_set(js, gpio, "mode", js_import(js, (uintptr_t) func1, "iii"); // Create gpio.mode(pin, mode)
305
- js_set(js, gpio, "read", js_import(js, (uintptr_t) func2, "ii"); // Create gpio.read(pin)
306
- js_set(js, gpio, "write", js_import(js, (uintptr_t) func3, "iii"); // Create gpio.write(pin, value)
252
+ ```c
253
+ jsval_t js_checkargs(struct js *js, jsval_t *args, int nargs, const char *spec, ...);
307
254
```
308
255
309
- ### js\_ usage()
256
+ A helper function that fetches JS arguments into C values, according to
257
+ ` spec ` type specification. Return ` JS_ERR ` on error, or ` JS_UNDEF ` on success.
258
+ Supported specifiers:
259
+ - ` b ` for ` bool `
260
+ - ` d ` for ` double `
261
+ - ` i ` for ` char ` , ` short ` , ` int ` , ` long ` , and corresponding unsigned variants
262
+ - ` s ` for ` char * `
263
+ - ` j ` for ` jsval_t `
264
+
265
+ Usage example - a C function that implements a JS function
266
+ ` greater_than(number1, number2) ` :
310
267
311
268
``` c
312
- int js_usage (struct js * );
269
+ jsval_t js_gt (struct js * js, jsval_t * args, int nargs) {
270
+ double a, b;
271
+ jsval_t res = js_checkargs(js, args, nargs, "dd", &a, &b);
272
+ if (js_type(res) == JS_UNDEF) {
273
+ res = js_mkval(a > b ? JS_TRUE : JS_FALSE);
274
+ }
275
+ return res;
276
+ }
313
277
```
314
278
315
- Return memory usage percentage - a number between 0 and 100.
316
-
317
-
318
279
## LICENSE
319
280
320
281
Dual license: AGPLv3 or commercial. For commercial licensing, technical support
0 commit comments