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

Syntax - Control Flow (match, guard, error, seq) More...

#include "x-prim.h"
#include "x-eval.h"
#include "x-env.h"
#include "x-heap.h"
#include "x-type/ptr.h"
#include "x-type/str.h"
#include <setjmp.h>

Functions

static x_obj_tx_prim_match (x_obj_t *p_base, x_obj_t *p_args)
 
static x_obj_tx_prim_guard (x_obj_t *p_base, x_obj_t *p_args)
 
static x_obj_tx_prim_error (x_obj_t *p_base, x_obj_t *p_args)
 
static x_obj_tx_prim_seq (x_obj_t *p_base, x_obj_t *p_args)
 
x_obj_tx_syntax_control_register (x_obj_t *p_base, x_obj_t *p_args)
 

Detailed Description

Syntax - Control Flow (match, guard, error, seq)

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

Function Documentation

◆ x_prim_error()

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

Error signalling form. x-lang: (error value [text])

Signals an error with the given value (fexpr – arguments are explicitly evaluated via x_eargs). If a guard handler is installed, transfers control to it via longjmp. Otherwise falls through to a fatal error.

Parameters
p_baseBase (execution context).
p_argsUnevaluated argument list; expects (caller value [text]).
Returns
Does not return normally when a handler is installed.
Note
Falls through to x_obj_error for fatal output when no handler exists.

The optional second argument is the uncaught report. A guard receives the VALUE – structured errors want to arrive as objects, and (Err tag e) depends on it – but an uncaught non-string value used to print as the bare literal "error", because this layer cannot render an arbitrary object and the object's own fields are the class layer's business (x-lang#211). Every message the x-lang library raises was invisible that way. Rather than teach the evaluator what an Err looks like, the raiser hands down a string to print: the prose stays where prose belongs, and C only carries it.

See also
x_prim_guard

◆ x_prim_guard()

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

Error recovery form. x-lang: (guard (var handler-body ...) body ...)

Installs an error handler, evaluates body, and catches errors (fexpr – clause and body forms are not evaluated up front). On error, restores the save stack to the guard point, binds the error value to var in a child of the guard's environment, and evaluates handler-body there.

Parameters
p_baseBase (execution context).
p_argsUnevaluated argument list; expects (caller (var handler-body ...) body ...).
Returns
Result of body on success, or result of handler-body on error.

Handler pair tree structure. The handler object is a nested pair tree:

(jmp-ptr . ((saved-env . nil) . (error-value . nil)))
#define nil
Definition x-eval.c:662
  • jmp-ptr: x_ptr wrapping a jmp_buf on the C stack
  • saved-env: the environment current at guard installation
  • error-value: initially nil, filled by x_eval_error or x_prim_error before longjmp

    Handler stack. The handler is pushed onto error_handler (a single-slot stack on p_base) and the previous handler is saved in p_prev_handler. On exit (normal or error), the previous handler is restored. This provides nested guard support – inner guards catch first.

    setjmp/longjmp protocol. setjmp(jmp) returns 0 on installation (normal path: evaluate body). When x_eval_error or x_prim_error calls longjmp, setjmp returns non-zero (error path). On the error path:

  1. save_stack is restored to the guard point (unwinding any fn/let frames entered since guard)
  2. error value is bound to var in a child of the saved environment, which is made current
  3. handler-body is evaluated via x_eval_body (no TCO – the guard frame must remain on the C stack)
  4. the saved environment is made current again
Note
Uses setjmp/longjmp for non-local error transfer. The jmp_buf lives on this C frame, so the handler is only valid while this function is on the call stack. Capturing and invoking it after return would be undefined behaviour.
The body is evaluated with x_eval_body (no TCO), not x_eval_body_tco. This ensures the guard's C frame (and its jmp_buf) remains valid throughout body execution.
See also
x_eval_error – C-level error that longjmps to this handler
x_prim_error – x-lang (error msg) that longjmps to this handler

◆ x_prim_match()

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

Conditional dispatch form. x-lang: (match (test body) ...)

Evaluates each test in order (fexpr – clause structure is not evaluated, but each test expression is explicitly evaluated). Returns the body of the first clause whose test is truthy, via tail-call evaluation. Returns nil if no clause matches.

Parameters
p_baseBase (execution context).
p_argsUnevaluated argument list; expects (caller (test body) ...).
Returns
NULL; result delivered via TCO expr slot.

Tail position. The matching clause's body expression is stored in tco_expr rather than evaluated directly. This makes the last clause a proper tail call – x_eval's trampoline will pick it up and evaluate it without growing the C stack. Because match does not alter the environment, tco_env is left nil (simple TCO); the trampoline skips env restore.

Note
Only the body of the FIRST matching clause is deferred. Subsequent clauses are never evaluated. If no clause matches, tco_expr remains nil and x_eval returns NULL.
See also
x_eval – trampoline that consumes tco_expr

◆ x_prim_seq()

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

Sequence form. x-lang: (seq a b)

Evaluates the first expression for its side effects, then tail-call evaluates the second (fexpr – both arguments are explicitly evaluated internally). Roots the argument list during evaluation of the first expression to protect it from GC.

Parameters
p_baseBase (execution context).
p_argsUnevaluated argument list; expects (caller a b).
Returns
NULL; result of b delivered via TCO expr slot.
Note
Internal primitive; used by the compiler to sequence body forms.

◆ x_syntax_control_register()

x_obj_t * x_syntax_control_register ( x_obj_t p_base,
x_obj_t p_args 
)

Register control flow syntax primitives.

Binds: match, guard, error, seq.

Parameters
p_baseBase (execution context).
p_argsUnused.
Returns
p_base.