x-engine-c v0.2.13
The C engine for x-lang
Loading...
Searching...
No Matches
operative.c File Reference

Operative (fexpr / dynamic-scope combiner) type implementation. More...

#include "x-type/operative.h"
#include "x-eval.h"
#include "x-env.h"
#include "x-heap.h"

Functions

static x_obj_tx_type_operative_mark (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_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_tx_type_operative_save (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_operative_struct (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_operative_register (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_operative_make (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_operative_call (x_obj_t *p_base, x_obj_t *p_args)
 

Variables

static x_satom_t x_type_operative_mark_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_operative_mark })
 
static x_satom_t x_type_operative_units_obj = x_obj_set(NULL, X_OBJ_FLAG_NONE, { .i = 2 })
 
x_satom_t x_type_operative_name = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { .s = (x_char_t *)X_TYPE_OPERATIVE_NAME })
 
x_satom_t x_type_operative_make_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_operative_make })
 
x_satom_t x_type_operative_call_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_operative_call })
 
x_satom_t x_type_operative_struct_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_operative_struct })
 
x_satom_t x_type_operative_save_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_operative_save })
 

Detailed Description

Operative (fexpr / dynamic-scope combiner) type implementation.

Author
Jon Ruttan (jonru.nosp@m.ttan.nosp@m.@gmai.nosp@m.l.co.nosp@m.m)

Function Documentation

◆ x_make_operative()

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 on the heap.

Builds the state list (params . (envparam . (body . env))) and stores it in slot 1 of the two-unit callable layout.

Parameters
p_basex_obj_t* – Base (execution context)
flagsx_obj_flag_t – Object flags
p_paramsx_obj_t* – Formal parameter tree
p_envparamx_obj_t* – Environment parameter name (or nil)
p_bodyx_obj_t* – Body expression list
p_envx_obj_t* – Captured environment
Returns
Heap-allocated operative object
Note
Constructor direction is DELIBERATELY the reverse of the simple atom/int/ptr types (#248): there x_make_X packs its one payload into an arg list and delegates to the x_type_X_make handler. An operative carries four fields, and this C-ABI constructor is the hot path (x_mkop, called on every op form via closure.c), so it builds the state tree directly; x_type_operative_make – the rare generic (make-instance) handler – adapts an arg list to it. Flipping to the simple-type direction would round-trip every op construction through a pack-then-unpack for no gain.

◆ x_type_operative_call()

x_obj_t * x_type_operative_call ( x_obj_t p_base,
x_obj_t p_args 
)

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:

  • The body's environment is a child of the captured environment with the formals bound in it, and the env-param bound to the caller's environment as a value. The caller's locals are not on this chain, so they are invisible to the body by construction.

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.

Parameters
p_basex_obj_t* – Base (execution context)
p_argsx_obj_t* – (operative . unevaluated-args)
Returns
Result of the operative body

◆ x_type_operative_make()

x_obj_t * x_type_operative_make ( x_obj_t p_base,
x_obj_t p_args 
)

Type-dispatch make callback: construct an operative from x-lang args.

Expects args: (params envparam body env [flags]).

Parameters
p_basex_obj_t* – Base (execution context)
p_argsx_obj_t* – Construction arguments
Returns
New operative object

◆ x_type_operative_mark()

static x_obj_t * x_type_operative_mark ( x_obj_t p_base,
x_obj_t p_args 
)
static

GC mark callback for operative objects.

Only marks slot 1 (state list), not slot 0 (fn ptr).

Parameters
p_basex_obj_t* – Base (execution context)
p_argsx_obj_t* – (object . (flags))
Returns
NULL always

◆ x_type_operative_register()

x_obj_t * x_type_operative_register ( x_obj_t p_base,
x_obj_t p_args 
)

Register (or retrieve) the OPERATIVE type struct on p_base.

Parameters
p_basex_obj_t* – Base (execution context)
p_argsx_obj_t* – Unused
Returns
The registered type struct object

◆ x_type_operative_save()

x_obj_t * x_type_operative_save ( x_obj_t p_base,
x_obj_t p_args 
)

Build the OPERATIVE type struct descriptor.

Parameters
p_basex_obj_t* – Base (execution context)
p_argsx_obj_t* – Unused
Returns
Type struct pair list

◆ x_type_operative_struct()

x_obj_t * x_type_operative_struct ( x_obj_t p_base,
x_obj_t p_args 
)

Build the OPERATIVE type struct descriptor.

Variable Documentation

◆ x_type_operative_call_prim

◆ x_type_operative_make_prim

◆ x_type_operative_mark_prim

x_satom_t x_type_operative_mark_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_operative_mark })
static

◆ x_type_operative_name

◆ x_type_operative_save_prim

◆ x_type_operative_struct_prim

◆ x_type_operative_units_obj

x_satom_t x_type_operative_units_obj = x_obj_set(NULL, X_OBJ_FLAG_NONE, { .i = 2 })
static