|
x-engine-c v0.2.13
The C engine for x-lang
|
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_t * | x_prim_match (x_obj_t *p_base, x_obj_t *p_args) |
| static x_obj_t * | x_prim_guard (x_obj_t *p_base, x_obj_t *p_args) |
| static x_obj_t * | x_prim_error (x_obj_t *p_base, x_obj_t *p_args) |
| static x_obj_t * | x_prim_seq (x_obj_t *p_base, x_obj_t *p_args) |
| x_obj_t * | x_syntax_control_register (x_obj_t *p_base, x_obj_t *p_args) |
Syntax - Control Flow (match, guard, error, seq)
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.
| p_base | Base (execution context). |
| p_args | Unevaluated argument list; expects (caller value [text]). |
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.
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.
| p_base | Base (execution context). |
| p_args | Unevaluated argument list; expects (caller (var handler-body ...) body ...). |
Handler pair tree structure. The handler object is a nested pair tree:
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:
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.
| p_base | Base (execution context). |
| p_args | Unevaluated argument list; expects (caller (test body) ...). |
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.
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.
| p_base | Base (execution context). |
| p_args | Unevaluated argument list; expects (caller a b). |