|
x-engine-c v0.2.13
The C engine for x-lang
|
Environments – first-class values the evaluator binds and looks up in. More...
#include "x-obj.h"Go to the source code of this file.
Macros | |
The pair | |
| #define | x_env_bindings(E) x_firstobj(E) |
| #define | x_env_parent(E) x_restobj(E) |
| #define | x_env_isroot(B, E) x_obj_isnil((B), x_restobj(E)) |
Functions | |
| x_obj_t * | x_env_make (x_obj_t *p_base, x_obj_t *p_parent) |
| x_obj_t * | x_env_lookup (x_obj_t *p_base, x_obj_t *p_env, x_obj_t *p_sym) |
| x_obj_t * | x_env_bind (x_obj_t *p_base, x_obj_t *p_env, x_obj_t *p_sym, x_obj_t *p_val) |
| x_obj_t * | x_env_extend (x_obj_t *p_base, x_obj_t *p_parent, x_obj_t *p_params, x_obj_t *p_vals) |
Environments – first-class values the evaluator binds and looks up in.
An environment is one pair, (bindings . parent). The root's parent is nil and its bindings are a tree (x-alist.c's BST, for the size of a loaded library); every other environment's bindings are an alist of (name . value) cells and its parent is the environment it was made in. A procedure call makes a child of the closure's environment; an operative body runs in a child of its static environment and receives the caller's environment as a value; def binds in the current environment; eval with an environment makes that one current. That is the whole of scope, and these four operations are all of it.
| #define x_env_bindings | ( | E | ) | x_firstobj(E) |
The alist, or the root's tree.
| #define x_env_isroot | ( | B, | |
| E | |||
| ) | x_obj_isnil((B), x_restobj(E)) |
A root has no parent.
| #define x_env_parent | ( | E | ) | x_restobj(E) |
The enclosing environment, nil at the root.
Bind p_sym to p_val in p_env itself: an existing binding there is updated in place, otherwise one is added. Never touches a parent. Returns p_val.
Bind p_sym to p_val in p_env itself.
A binding the environment already holds is updated in place; otherwise one is added – to the root's tree, or in front of another environment's alist. A parent's binding of the same name is never touched: it is shadowed, which is what a definition in a child means. def is this on the current environment; the C binding doors and base bind are this on a root.
| p_base | x_obj_t* – Base (execution context) |
| p_env | x_obj_t* – The environment to bind in |
| p_sym | x_obj_t* – The symbol |
| p_val | x_obj_t* – The value |
p_val | x_obj_t * x_env_extend | ( | x_obj_t * | p_base, |
| x_obj_t * | p_parent, | ||
| x_obj_t * | p_params, | ||
| x_obj_t * | p_vals | ||
| ) |
Make a child of p_parent with p_params bound to p_vals: the environment a procedure body or an operative body runs in.
Make a child environment with parameters bound to values.
The environment a procedure body or an operative body runs in: a fresh (bindings . parent) pair whose parent is p_parent, the closure's or the operative's static environment, and whose bindings are the parameters. Handles three cases: (1) variadic – a bare symbol binds to the entire remaining value list, (2) base – no more params, (3) one parameter to one value, then the rest.
| p_base | x_obj_t* – Base (execution context) |
| p_parent | x_obj_t* – The environment the new one is a child of |
| p_params | x_obj_t* – Parameter list (or single symbol for variadic) |
| p_vals | x_obj_t* – Value list |
The parent is never modified. The bindings are new cells in the new environment; p_parent is only pointed at. Fewer values than parameters binds the remainder to nil, symmetric with surplus values, which are ignored once the parameters run out.
(fn (a . rest) ...).def, the same binder one name at a time The (name . value) cell binding p_sym in p_env or an ancestor, or NULL when no environment on the chain binds it. Names are found by identity; a symbol interned in another base stands for this base's own symbol of its spelling.
The cell binding p_sym in p_env or an ancestor.
Walks from p_env to the root: an environment with a parent searches its alist of (name . value) cells by symbol identity; the root searches its tree, also by identity. The first hit wins, so a child's binding shadows a parent's. This is the whole of symbol lookup – x_type_symbol_eval and set! call nothing else.
Symbols intern per base, and a name is found by identity, not by spelling: a base's own symbol finds only what was bound under it, so a name the host bound into a child under the host's symbol is not found by the child's symbol of the same spelling. A FOREIGN symbol – one interned in another base, as every symbol of a form the host read and evaluates in a child is – has no identity here, so it stands for this base's own symbol of its spelling, and that is what is looked up. That is what lets (base eval B (lit (+ 2 3))) hand a child the host's + and reach the child's binding of its own +. The retry runs only when the identity lookup at the root missed.
| p_base | x_obj_t* – Base (execution context) |
| p_env | x_obj_t* – The environment to start from |
| p_sym | x_obj_t* – The symbol |
(name . value) cell, or NULL when unbound Make an empty environment whose parent is p_parent (nil for a root).
Make an empty environment whose parent is p_parent.
An environment is one pair, (bindings . parent). A nil parent makes a root, whose bindings are kept as a tree; any other environment keeps an alist. x_eval_make builds the base's root this way; procedure and operative calls make children through x_env_extend; guard makes one for its error variable.
| p_base | x_obj_t* – Base (execution context) |
| p_parent | x_obj_t* – The enclosing environment, or nil for a root |