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

Procedure (applicative closure) type implementation. More...

#include "x-type/procedure.h"
#include "x-eval.h"
#include "x-env.h"
#include "x-tco.h"
#include "x-heap.h"
#include "x-obj/prim.h"
#include "x-prim.h"

Functions

static x_obj_tx_type_procedure_mark (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_make_procedure (x_obj_t *p_base, x_obj_flag_t flags, x_obj_t *p_params, x_obj_t *p_body, x_obj_t *p_env)
 
x_obj_tx_type_procedure_save (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_procedure_struct (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_procedure_register (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_procedure_make (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_procedure_call (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_type_procedure_apply (x_obj_t *p_base, x_obj_t *p_args)
 

Variables

static x_satom_t x_type_procedure_mark_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_procedure_mark })
 
static x_satom_t x_type_procedure_units_obj = x_obj_set(NULL, X_OBJ_FLAG_NONE, { .i = 2 })
 
x_satom_t x_type_procedure_name = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { .s = (x_char_t *)X_TYPE_PROCEDURE_NAME })
 
x_satom_t x_type_procedure_make_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_procedure_make })
 
x_satom_t x_type_procedure_call_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_procedure_call })
 
x_satom_t x_type_procedure_struct_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_procedure_struct })
 
x_satom_t x_type_procedure_save_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_procedure_save })
 

Detailed Description

Procedure (applicative closure) type implementation.

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

Function Documentation

◆ x_make_procedure()

x_obj_t * x_make_procedure ( x_obj_t p_base,
x_obj_flag_t  flags,
x_obj_t p_params,
x_obj_t p_body,
x_obj_t p_env 
)

Allocate a new procedure (closure) on the heap.

Builds the state list (params . (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 (e.g. X_OBJ_FLAG_WRAP)
p_paramsx_obj_t* – Formal parameter tree
p_bodyx_obj_t* – Body expression list
p_envx_obj_t* – Captured lexical environment
Returns
Heap-allocated procedure object
Note
Constructor direction is DELIBERATELY the reverse of the simple atom/int/ptr types (#248) – see x_make_operative. A procedure carries four fields and this is the hot path (x_mkproc, called on every fn form via closure.c), so it builds the state tree directly; x_type_procedure_make – the rare generic handler – adapts an arg list to it.

◆ x_type_procedure_apply()

x_obj_t * x_type_procedure_apply ( x_obj_t p_base,
x_obj_t p_args 
)

Non-TCO apply path for (apply f args).

Arguments are already evaluated. Saves and restores the full environment state around body evaluation.

Unlike x_type_procedure_call, this path does NOT use the TCO trampoline. It calls x_eval_body (not x_eval_body_tco), which means the C call stack grows with each nested apply. This is necessary because apply is called from contexts where the caller needs the result immediately (e.g. map, fold, for-each).

The current environment is saved to a local before body evaluation and made current again afterward.

Parameters
p_basex_obj_t* – Base (execution context)
p_argsx_obj_t* – (procedure . evaluated-args)
Returns
Result of the procedure body
See also
x_type_procedure_call for the TCO path

◆ x_type_procedure_call()

x_obj_t * x_type_procedure_call ( x_obj_t p_base,
x_obj_t p_args 
)

Type-dispatch call callback: evaluate a procedure application (TCO).

For wrapped combiners (applicatives), dispatches to the underlying combiner with evaluated arguments. For plain closures, extends the environment, pushes a save-stack frame, and enters the body via TCO.

Two dispatch paths based on X_OBJ_FLAG_WRAP:

Wrapped applicative (WRAP flag set): The procedure is a thin wrapper around another combiner stored in the env slot. Args are evaluated, then the underlying combiner is called via x_obj_prim_call. This is how (wrap op) creates an applicative from an operative.

Plain closure (no WRAP flag): Pushes the caller's environment onto the save-stack, makes a child of the closure's environment with the parameters bound current, and enters the body via x_eval_body_tco for tail-call optimization – the trampoline loop in x_eval makes the saved environment current again when the TCO chain completes.

Parameters
p_basex_obj_t* – Base (execution context)
p_argsx_obj_t* – (procedure . unevaluated-args)
Returns
Result of the procedure body
See also
x_type_procedure_apply for the non-TCO path

◆ x_type_procedure_make()

x_obj_t * x_type_procedure_make ( x_obj_t p_base,
x_obj_t p_args 
)

Type-dispatch make callback: construct a procedure from x-lang args.

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

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

◆ x_type_procedure_mark()

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

GC mark callback for procedure objects.

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

Procedure heap layout has two data slots:

x_obj_data_i(p_obj, 0) = fn_ptr (raw C pointer, NOT a heap obj)
x_obj_data_i(p_obj, 1) = state (pair tree -- must be marked)
#define pair(X, Y)
Definition x-eval.c:663
#define x_obj_data_i(X, I)
Definition x-obj.h:330

Slot 0 is skipped because it holds a raw x_fn_t cast to x_obj_t*. Passing it to x_heap_tree_mark would chase an invalid heap pointer and corrupt the mark bitmap or segfault. Only slot 1 (the state pair list) contains GC-managed objects that need marking.

Parameters
p_basex_obj_t* – Base (execution context)
p_argsx_obj_t* – (object . (flags))
Returns
NULL always
See also
x_type_procedure_call for the callable stored in slot 0

◆ x_type_procedure_register()

x_obj_t * x_type_procedure_register ( x_obj_t p_base,
x_obj_t p_args 
)

Register (or retrieve) the PROCEDURE 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_procedure_save()

x_obj_t * x_type_procedure_save ( x_obj_t p_base,
x_obj_t p_args 
)

Build the PROCEDURE type struct descriptor.

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

◆ x_type_procedure_struct()

x_obj_t * x_type_procedure_struct ( x_obj_t p_base,
x_obj_t p_args 
)

Build the PROCEDURE type struct descriptor.

Variable Documentation

◆ x_type_procedure_call_prim

◆ x_type_procedure_make_prim

◆ x_type_procedure_mark_prim

x_satom_t x_type_procedure_mark_prim = x_obj_set(x_type_atom_obj, X_OBJ_FLAG_NONE, { (x_obj_t *)&x_type_procedure_mark })
static

◆ x_type_procedure_name

◆ x_type_procedure_save_prim

◆ x_type_procedure_struct_prim

◆ x_type_procedure_units_obj

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