2 changed files with 125 additions and 0 deletions
@ -0,0 +1,124 @@
|
||||
|
||||
# C API |
||||
To use `fe` in a project a `fe_Context` must first be intialized; |
||||
this is done by using the `fe_open()` function. The function expects a |
||||
block of memory (typically greater than 16kb), the block is used by the |
||||
context to store objects and context state and should remain valid for |
||||
the lifetime of the context. `fe_close()` should be called when you are |
||||
finished with a context, this will assure any `ptr` objects are properly |
||||
garbage collected. |
||||
|
||||
```c |
||||
int size = 1024 * 1024; |
||||
void *data = malloc(size); |
||||
|
||||
fe_Context *ctx = fe_open(data, size); |
||||
|
||||
/* ... */ |
||||
|
||||
fe_close(ctx); |
||||
free(data); |
||||
``` |
||||
|
||||
|
||||
## Running a script |
||||
To run a script it should first be read then evaluated; this should be |
||||
done in a loop if there are several root-level expressions contained in |
||||
the script. `fe_readfp()` is provided as a convenience to read from a |
||||
file pointer; `fe_read()` can be used with a custom `fe_ReadFn` callback |
||||
function to read from other sources. |
||||
|
||||
```c |
||||
FILE *fp = fopen("test.fe", "rb"); |
||||
int gc = fe_savegc(ctx); |
||||
|
||||
for (;;) { |
||||
fe_Object *obj = fe_readfp(ctx, fp); |
||||
|
||||
/* break if there's nothing left to read */ |
||||
if (!obj) { break; } |
||||
|
||||
/* evaluate read object */ |
||||
fe_eval(ctx, obj); |
||||
|
||||
/* restore GC stack which would now contain both the read object and |
||||
** result from evaluation */ |
||||
fe_restoregc(ctx, gc); |
||||
} |
||||
|
||||
fclose(fp); |
||||
``` |
||||
|
||||
|
||||
## Calling a function |
||||
A function can be called by creating a list and evaulating it; for |
||||
example, we could add two numbers using the `+` function: |
||||
|
||||
```c |
||||
int gc = fe_savegc(ctx); |
||||
|
||||
fe_Object *objs[3]; |
||||
objs[0] = fe_symbol(ctx, "+"); |
||||
objs[1] = fe_number(ctx, 10); |
||||
objs[2] = fe_number(ctx, 20); |
||||
|
||||
fe_Object *res = fe_eval(ctx, fe_list(ctx, objs, 3)); |
||||
printf("result: %g\n", fe_tonumber(ctx, res)); |
||||
|
||||
/* discard all temporary objects pushed to the GC stack */ |
||||
fe_restoregc(ctx, gc); |
||||
``` |
||||
|
||||
|
||||
## Creating a cfunc |
||||
A `cfunc` can be created by using the `fe_cfunc()` function with a |
||||
`fe_CFunc` function argument. The `cfunc` can be bound to a global |
||||
variable by using the `fe_set()` function. `cfunc`s take a context and |
||||
argument list as its arguments and returns a result object. The result |
||||
should never be `NULL`; in the case of wanting to return `nil` the value |
||||
returned by `fe_bool(ctx, 0)` should be used. |
||||
|
||||
The `pow` function from `math.h` could be wrapped as such: |
||||
|
||||
```c |
||||
static fe_Object* f_pow(fe_Context *ctx, fe_Object *arg) { |
||||
float x = fe_tonumber(ctx, fe_nextarg(ctx, &arg)); |
||||
float y = fe_tonumber(ctx, fe_nextarg(ctx, &arg)); |
||||
return fe_number(ctx, pow(x, y)); |
||||
} |
||||
|
||||
fe_set(ctx, fe_symbol(ctx, "pow"), fe_cfunc(ctx, f_pow)); |
||||
``` |
||||
|
||||
The `cfunc` could then be called like any other function: |
||||
|
||||
```clojure |
||||
(print (pow 2 10)) |
||||
``` |
||||
|
||||
|
||||
## Creating a ptr |
||||
The `ptr` object type is provided to allow for custom objects. By default |
||||
no type checking is performed and thus pointers must be wrapped by the |
||||
user and tagged to assure type safety if more than one type of pointer |
||||
is used. |
||||
|
||||
A `ptr` object can be created by using the `fe_ptr()` function. |
||||
|
||||
The `gc` and `mark` handlers are provided for dealing with `ptr`s |
||||
regarding garbage collection. Whenever a `ptr` is marked by the GC the |
||||
`mark` handler is called on it — this is useful if the `ptr` stores |
||||
additional objects which also need to be marked via `fe_mark()`. The |
||||
`gc` handler is called on the `ptr` when it becomes unreachable and is |
||||
garbage collected, such that the resources used by the `ptr` can be |
||||
freed. The handlers can be set by setting the relevant fields in the |
||||
struct returned by `fe_handlers()`. |
||||
|
||||
|
||||
## Error handling |
||||
When an error occurs the `fe_error()` is called; by default, the |
||||
error and stack traceback is printed and the program exited. If you want |
||||
to recover from an error the `error` handler field in the struct returned |
||||
by `fe_handlers()` can be set and `longjmp()` can be used to exit the |
||||
handler; the context is left in a safe state and can continue to be |
||||
used. New `fe_Object`s should not be created inside the error handler. |
Loading…
Reference in new issue