Representing compilation contexts, and holding compiled values.

§1. Value holsters were created as a shim at a time when almost the entire Inform code-base had to be turned around. Pre-2017, Inform generated code by writing Inform 6 syntax out by hand: but post-2019, Inter bytecode was generated instead. Hundreds of subsystems had to be rewritten, a process taking over a year, and during this time the compiler had to work in a piebald sort of way — some systems generating bytecode, others still pouring our raw I6 as text.

The value_holster was invented as a way to manage this. It allows the caller of a compilation function to ask for code to be made in a particular way: this is the vhmode_wanted field. These ways are:

So much for what the caller wants. The compilation function, or subsystem, then does whatever it does, and sets vhmode_provided to the mode it actually compiled in; the caller can then deal with the situation arising if it wasn't what was wanted. During 2017 this often meant that the function would write out some raw I6 syntax, and reply INTER_TEXT_VHMODE to signal this; the caller would then turn this into Inter by wrapping it up as either a "splat" or a "glob".

With the transition now complete, INTER_TEXT_VHMODE no longer exists. But value holsters continue to be a useful device.

enum INTER_DATA_VHMODE from 1
enum INTER_VAL_VHMODE
enum INTER_VOID_VHMODE
enum NO_VHMODE
typedef struct value_holster {
    int vhmode_wanted;
    int vhmode_provided;
    inter_pair val;
} value_holster;

§2.

value_holster Holsters::new(int vhm) {
    value_holster vh;
    vh.val = InterValuePairs::undef();
    vh.vhmode_wanted = vhm;
    vh.vhmode_provided = NO_VHMODE;  the compilation function has not yet set this
    if (vhm == NO_VHMODE) internal_error("can't want NO_VHMODE");
    return vh;
}

§3. A compilation function can produce, as its output, a value in either INTER_DATA_VHMODE (where this is exactly what is wanted) or in INTER_VAL_VHMODE (where it can easily be adapted).

int Holsters::value_pair_allowed(value_holster *vh) {
    if (vh == NULL) internal_error("no VH");
    if ((vh->vhmode_wanted == INTER_DATA_VHMODE) ||
        (vh->vhmode_wanted == INTER_VAL_VHMODE)) return TRUE;
    return FALSE;
}

§4. This is how a compilation function "holsters" a value pair:

void Holsters::holster_pair(value_holster *vh, inter_pair val) {
    if (vh == NULL) internal_error("no VH");
    vh->val = val;
    vh->vhmode_provided = INTER_DATA_VHMODE;
}

§5. And this is how the caller "unholsters" that value, after the function has returned. If we find NO_VHMODE, we convert that to INTER_DATA_VHMODE with the literal number value 0.

On exit, the provided mode is always INTER_DATA_VHMODE.

A second or subsequent call on the same holster does nothing, except to return the same value pair, which is still stored in it. (In that sense, these aren't really like a gunslinger's holster, where a revolver once drawn is no longer in the holster.)

inter_pair Holsters::unholster_to_pair(value_holster *vh) {
    if (vh == NULL) internal_error("no VH");
    switch (vh->vhmode_provided) {
        case INTER_DATA_VHMODE:
            break;
        case INTER_VAL_VHMODE:
            internal_error("impossible to unholster pair for compiled val code");
            break;
        case INTER_VOID_VHMODE:
            internal_error("impossible to unholster pair for compiled void code");
            break;
        case NO_VHMODE:
            vh->vhmode_provided = INTER_DATA_VHMODE;
            vh->val = InterValuePairs::number(0);
            break;
    }
    return vh->val;
}

§6. If, on the other hand, the caller was asking for INTER_VAL_VHMODE, it should make use of the following. If we find NO_VHMODE, we compile a val producing the literal value 0; if we find INTER_DATA_VHMODE, we compile a val producing whatever value was holstered.

On exit, the provided mode is always INTER_VAL_VHMODE.

A second or subsequent call on the same holster does nothing.

void Holsters::unholster_to_code_val(inter_tree *I, value_holster *vh) {
    if (vh == NULL) internal_error("no VH");
    switch (vh->vhmode_provided) {
        case INTER_DATA_VHMODE:
        case NO_VHMODE: {
            inter_pair val = Holsters::unholster_to_pair(vh);
            Produce::val(I, K_value, val);
            vh->vhmode_provided = INTER_VAL_VHMODE;
            break;
        }
        case INTER_VAL_VHMODE:
            break;
        case INTER_VOID_VHMODE:
            internal_error("impossible to use void Inter code in val context");
            break;
    }
}