|
x-engine-c v0.2.13
The C engine for x-lang
|
Operative (fexpr / dynamic-scope combiner) type for x-lang. More...
Go to the source code of this file.
Macros | |
| #define | X_TYPE_OPERATIVE_NAME "OPERATIVE" |
Type predicates | |
| #define | x_obj_type_isoperative(B, X) x_obj_is_type((B), (X), X_TYPE_OPERATIVE_NAME) |
State accessors | |
Operative state list: (params . (envparam . (body . env))). Stored in x_callable_state (slot 1) of [fn-ptr][state] layout. GC traverses via the p_units=2 fallback in x_type_heap_mark. | |
| #define | x_opstate(X) x_callable_state((X)) |
| #define | x_opparams(X) x_firstobj(x_opstate((X))) |
| #define | x_openvparam(X) x_firstobj(x_restobj(x_opstate((X)))) |
| #define | x_opbody(X) x_firstobj(x_restobj(x_restobj(x_opstate((X))))) |
| #define | x_openv(X) x_restobj(x_restobj(x_restobj(x_opstate((X))))) |
Convenience constructors | |
| #define | x_mkop(B, P, EP, BD, E) x_make_operative((B), X_OBJ_FLAG_NONE, (P), (EP), (BD), (E)) |
Functions | |
| x_obj_t * | x_make_operative (x_obj_t *p_base, x_obj_flag_t flags, x_obj_t *p_params, x_obj_t *p_envparam, x_obj_t *p_body, x_obj_t *p_env) |
| x_obj_t * | x_type_operative_register (x_obj_t *p_base, x_obj_t *p_args) |
| x_obj_t * | x_type_operative_struct (x_obj_t *p_base, x_obj_t *p_args) |
| x_obj_t * | x_type_operative_make (x_obj_t *p_base, x_obj_t *p_args) |
| x_obj_t * | x_type_operative_call (x_obj_t *p_base, x_obj_t *p_args) |
Variables | |
Static primitive atoms for the type struct. | |
| x_satom_t | x_type_operative_name |
| x_satom_t | x_type_operative_make_prim |
| x_satom_t | x_type_operative_call_prim |
| x_satom_t | x_type_operative_struct_prim |
Operative (fexpr / dynamic-scope combiner) type for x-lang.
An operative is a two-unit heap object with callable layout:
| #define x_mkop | ( | B, | |
| P, | |||
| EP, | |||
| BD, | |||
| E | |||
| ) | x_make_operative((B), X_OBJ_FLAG_NONE, (P), (EP), (BD), (E)) |
Make operative with default flags.
| #define x_obj_type_isoperative | ( | B, | |
| X | |||
| ) | x_obj_is_type((B), (X), X_TYPE_OPERATIVE_NAME) |
Test if object is an operative.
| #define x_opbody | ( | X | ) | x_firstobj(x_restobj(x_restobj(x_opstate((X))))) |
Body expression list.
| #define x_openvparam | ( | X | ) | x_firstobj(x_restobj(x_opstate((X)))) |
Environment parameter name.
| #define x_opparams | ( | X | ) | x_firstobj(x_opstate((X))) |
Parameter tree.
| #define x_opstate | ( | X | ) | x_callable_state((X)) |
Full state list.
| #define X_TYPE_OPERATIVE_NAME "OPERATIVE" |
Type name string.
| x_obj_t * x_make_operative | ( | x_obj_t * | p_base, |
| x_obj_flag_t | flags, | ||
| x_obj_t * | p_params, | ||
| x_obj_t * | p_envparam, | ||
| x_obj_t * | p_body, | ||
| x_obj_t * | p_env | ||
| ) |
Allocate a new operative object on the heap.
Allocate a new operative on the heap.
Builds the state list (params . (envparam . (body . env))) and stores it in slot 1 of the two-unit callable layout.
| p_base | x_obj_t* – Base (execution context) |
| flags | x_obj_flag_t – Object flags |
| p_params | x_obj_t* – Formal parameter tree |
| p_envparam | x_obj_t* – Environment parameter name (or nil) |
| p_body | x_obj_t* – Body expression list |
| p_env | x_obj_t* – Captured environment |
Type-dispatch call callback – evaluate an operative application.
Type-dispatch call callback: evaluate an operative application.
Operatives are lexically scoped: the body runs in the env captured at (op ...) creation time, extended with the formals bound to the UNEVALUATED arguments. The caller's environment is exposed only through the named env-param (when the op declares one), so that (eval expr e) or (tail-eval expr e) inside the body can reach back into the caller's scope explicitly. Caller-local names cannot silently shadow the globals the body relies on. Operatives do NOT get self-passing.
Implementation:
Body is run via x_eval_body (NOT a TCO body evaluator). Each body form is evaluated synchronously to completion – tail-eval inside a form still TCOs via x_eval's own trampoline, so common patterns like (tail-eval (...) e) work without growing the C stack. The op itself doesn't TCO into its caller, but lib ops typically dispatch the heavy work via tail-eval into the caller's env, so the op frame isn't on the recursion path.
After the body returns, the caller's environment is made current again, unconditionally. A (def ...) the body evaluated in the caller's environment is IN that environment, so restoring the pointer keeps it.
| p_base | x_obj_t* – Base (execution context) |
| p_args | x_obj_t* – (operative . unevaluated-args) |
Type-dispatch make callback for OPERATIVE.
Type-dispatch make callback: construct an operative from x-lang args.
Expects args: (params envparam body env [flags]).
| p_base | x_obj_t* – Base (execution context) |
| p_args | x_obj_t* – Construction arguments |
Register (or retrieve) the OPERATIVE type struct on p_base.
Register (or retrieve) the OPERATIVE type struct on p_base.
| p_base | x_obj_t* – Base (execution context) |
| p_args | x_obj_t* – Unused |
Build the OPERATIVE type struct descriptor.
| x_satom_t x_type_operative_call_prim |
| x_satom_t x_type_operative_make_prim |
|
extern |
| x_satom_t x_type_operative_struct_prim |