The standard set of primitive invocations available in Inter code as generated by the Inform tool-chain.


§1. The standard Inform 7 instruction set. Metadata on the primitives used by Inter in the Inform tool-set is stored as an array standard_inform7_primitives of the following records:

typedef struct inform7_primitive {
    inter_ti BIP;
    char *name_c;
    char *signature_c;
    struct text_stream *name;
    struct text_stream *signature;
    int term_count;
    int term_categories[8];
    int takes_code_blocks;
} inform7_primitive;

§2. Each different primitive has a unique BIP number. (The origins of the term BIP are now lost, though the I and the P presumably once stood for "Inform" and "primitive".) BIPs count upwards contiguously from 1.

The point of BIPs is simply that it would be too slow to index primitives only by their instruction names, because even with all the hashing in the world, there would have to be string comparisons.

define MAX_BIPS 100
enumerate PLUS_BIP 1
enumerate MINUS_BIP 
enumerate UNARYMINUS_BIP 
enumerate TIMES_BIP 
enumerate DIVIDE_BIP 
enumerate MODULO_BIP 
enumerate BITWISEAND_BIP 
enumerate BITWISEOR_BIP 
enumerate BITWISENOT_BIP 
enumerate SEQUENTIAL_BIP 
enumerate TERNARYSEQUENTIAL_BIP 
enumerate RANDOM_BIP 
enumerate STORE_BIP 
enumerate PREINCREMENT_BIP 
enumerate POSTINCREMENT_BIP 
enumerate PREDECREMENT_BIP 
enumerate POSTDECREMENT_BIP 
enumerate SETBIT_BIP 
enumerate CLEARBIT_BIP 
enumerate PUSH_BIP 
enumerate PULL_BIP 
enumerate LOOKUP_BIP 
enumerate LOOKUPBYTE_BIP 
enumerate PROPERTYARRAY_BIP 
enumerate PROPERTYLENGTH_BIP 
enumerate PROPERTYEXISTS_BIP 
enumerate PROPERTYVALUE_BIP 
enumerate MOVE_BIP 
enumerate REMOVE_BIP 
enumerate CHILD_BIP 
enumerate CHILDREN_BIP 
enumerate SIBLING_BIP 
enumerate PARENT_BIP 
enumerate METACLASS_BIP 
enumerate NOT_BIP 
enumerate AND_BIP 
enumerate OR_BIP 
enumerate EQ_BIP 
enumerate NE_BIP 
enumerate GT_BIP 
enumerate GE_BIP 
enumerate LT_BIP 
enumerate LE_BIP 
enumerate OFCLASS_BIP 
enumerate IN_BIP 
enumerate NOTIN_BIP 
enumerate ALTERNATIVE_BIP 
enumerate ENABLEPRINTING_BIP 
enumerate FONT_BIP 
enumerate STYLE_BIP 
enumerate PRINT_BIP 
enumerate PRINTNL_BIP 
enumerate PRINTCHAR_BIP 
enumerate PRINTOBJ_BIP 
enumerate PRINTNUMBER_BIP 
enumerate PRINTDWORD_BIP 
enumerate PRINTSTRING_BIP 
enumerate BOX_BIP 
enumerate SPACES_BIP 
enumerate IF_BIP 
enumerate IFDEBUG_BIP 
enumerate IFSTRICT_BIP 
enumerate IFELSE_BIP 
enumerate WHILE_BIP 
enumerate DO_BIP 
enumerate FOR_BIP 
enumerate OBJECTLOOP_BIP 
enumerate OBJECTLOOPX_BIP 
enumerate BREAK_BIP 
enumerate CONTINUE_BIP 
enumerate SWITCH_BIP 
enumerate CASE_BIP 
enumerate ALTERNATIVECASE_BIP 
enumerate DEFAULT_BIP 
enumerate RETURN_BIP 
enumerate JUMP_BIP 
enumerate QUIT_BIP 
enumerate RESTORE_BIP 
enumerate INDIRECT0V_BIP 
enumerate INDIRECT1V_BIP 
enumerate INDIRECT2V_BIP 
enumerate INDIRECT3V_BIP 
enumerate INDIRECT4V_BIP 
enumerate INDIRECT5V_BIP 
enumerate INDIRECT6V_BIP 
enumerate INDIRECT0_BIP 
enumerate INDIRECT1_BIP 
enumerate INDIRECT2_BIP 
enumerate INDIRECT3_BIP 
enumerate INDIRECT4_BIP 
enumerate INDIRECT5_BIP 
enumerate INDIRECT6_BIP 
enumerate MESSAGE0_BIP 
enumerate MESSAGE1_BIP 
enumerate MESSAGE2_BIP 
enumerate MESSAGE3_BIP 
enumerate EXTERNALCALL_BIP 

§3. And here is the array of metadata. It's tiresome to have to include the null fields here, which is done only because text_stream literals I"like this" cannot be compiled in a constant context by the clang C compiler. So the names and signatures of the primitives are compiled as char * literals instead, and converted on first use at runtime.

It is essential that the sequence of rows below is the same as the sequence of enumerations above; but Primitives::prepare_standard_set_array checks this at runtime and throws an error if not.

define SIP_RECORD_END 
    NULL, NULL, 0,
    { VAL_PRIM_CAT, VAL_PRIM_CAT, VAL_PRIM_CAT, VAL_PRIM_CAT, VAL_PRIM_CAT, VAL_PRIM_CAT, VAL_PRIM_CAT, VAL_PRIM_CAT },
    FALSE
inform7_primitive standard_inform7_primitives[] = {
    { PLUS_BIP,              "!plus",              "val val -> val",                  SIP_RECORD_END },
    { MINUS_BIP,             "!minus",             "val val -> val",                  SIP_RECORD_END },
    { UNARYMINUS_BIP,        "!unaryminus",        "val -> val",                      SIP_RECORD_END },
    { TIMES_BIP,             "!times",             "val val -> val",                  SIP_RECORD_END },
    { DIVIDE_BIP,            "!divide",            "val val -> val",                  SIP_RECORD_END },
    { MODULO_BIP,            "!modulo",            "val val -> val",                  SIP_RECORD_END },
    { BITWISEAND_BIP,        "!bitwiseand",        "val val -> val",                  SIP_RECORD_END },
    { BITWISEOR_BIP,         "!bitwiseor",         "val val -> val",                  SIP_RECORD_END },
    { BITWISENOT_BIP,        "!bitwisenot",        "val -> val",                      SIP_RECORD_END },
    { SEQUENTIAL_BIP,        "!sequential",        "val val -> val",                  SIP_RECORD_END },
    { TERNARYSEQUENTIAL_BIP, "!ternarysequential", "val val val -> val",              SIP_RECORD_END },
    { RANDOM_BIP,            "!random",            "val -> val",                      SIP_RECORD_END },

    { STORE_BIP,             "!store",             "ref val -> val",                  SIP_RECORD_END },
    { PREINCREMENT_BIP,      "!preincrement",      "ref -> val",                      SIP_RECORD_END },
    { POSTINCREMENT_BIP,     "!postincrement",     "ref -> val",                      SIP_RECORD_END },
    { PREDECREMENT_BIP,      "!predecrement",      "ref -> val",                      SIP_RECORD_END },
    { POSTDECREMENT_BIP,     "!postdecrement",     "ref -> val",                      SIP_RECORD_END },
    { SETBIT_BIP,            "!setbit",            "ref val -> void",                 SIP_RECORD_END },
    { CLEARBIT_BIP,          "!clearbit",          "ref val -> void",                 SIP_RECORD_END },

    { PUSH_BIP,              "!push",              "val -> void",                     SIP_RECORD_END },
    { PULL_BIP,              "!pull",              "ref -> void",                     SIP_RECORD_END },
    { LOOKUP_BIP,            "!lookup",            "val val -> val",                  SIP_RECORD_END },
    { LOOKUPBYTE_BIP,        "!lookupbyte",        "val val -> val",                  SIP_RECORD_END },
    { PROPERTYARRAY_BIP,     "!propertyarray",     "val val val -> val",              SIP_RECORD_END },
    { PROPERTYLENGTH_BIP,    "!propertylength",    "val val val -> val",              SIP_RECORD_END },
    { PROPERTYEXISTS_BIP,    "!propertyexists",    "val val val -> val",              SIP_RECORD_END },
    { PROPERTYVALUE_BIP,     "!propertyvalue",     "val val val -> val",              SIP_RECORD_END },

    { MOVE_BIP,              "!move",              "val val -> void",                 SIP_RECORD_END },
    { REMOVE_BIP,            "!remove",            "val -> void",                     SIP_RECORD_END },
    { CHILD_BIP,             "!child",             "val -> val",                      SIP_RECORD_END },
    { CHILDREN_BIP,          "!children",          "val -> val",                      SIP_RECORD_END },
    { SIBLING_BIP,           "!sibling",           "val -> val",                      SIP_RECORD_END },
    { PARENT_BIP,            "!parent",            "val -> val",                      SIP_RECORD_END },
    { METACLASS_BIP,         "!metaclass",         "val -> val",                      SIP_RECORD_END },

    { NOT_BIP,               "!not",               "val -> val",                      SIP_RECORD_END },
    { AND_BIP,               "!and",               "val val -> val",                  SIP_RECORD_END },
    { OR_BIP,                "!or",                "val val -> val",                  SIP_RECORD_END },
    { EQ_BIP,                "!eq",                "val val -> val",                  SIP_RECORD_END },
    { NE_BIP,                "!ne",                "val val -> val",                  SIP_RECORD_END },
    { GT_BIP,                "!gt",                "val val -> val",                  SIP_RECORD_END },
    { GE_BIP,                "!ge",                "val val -> val",                  SIP_RECORD_END },
    { LT_BIP,                "!lt",                "val val -> val",                  SIP_RECORD_END },
    { LE_BIP,                "!le",                "val val -> val",                  SIP_RECORD_END },
    { OFCLASS_BIP,           "!ofclass",           "val val -> val",                  SIP_RECORD_END },
    { IN_BIP,                "!in",                "val val -> val",                  SIP_RECORD_END },
    { NOTIN_BIP,             "!notin",             "val val -> val",                  SIP_RECORD_END },
    { ALTERNATIVE_BIP,       "!alternative",       "val val -> val",                  SIP_RECORD_END },

    { ENABLEPRINTING_BIP,    "!enableprinting",    "void -> void",                    SIP_RECORD_END },
    { FONT_BIP,              "!font",              "val -> void",                     SIP_RECORD_END },
    { STYLE_BIP,             "!style",             "val -> void",                     SIP_RECORD_END },
    { PRINT_BIP,             "!print",             "val -> void",                     SIP_RECORD_END },
    { PRINTNL_BIP,           "!printnl",           "void -> void",                    SIP_RECORD_END },
    { PRINTCHAR_BIP,         "!printchar",         "val -> void",                     SIP_RECORD_END },
    { PRINTOBJ_BIP,          "!printobj",          "val -> void",                     SIP_RECORD_END },
    { PRINTNUMBER_BIP,       "!printnumber",       "val -> void",                     SIP_RECORD_END },
    { PRINTDWORD_BIP,        "!printdword",        "val -> void",                     SIP_RECORD_END },
    { PRINTSTRING_BIP,       "!printstring",       "val -> void",                     SIP_RECORD_END },
    { BOX_BIP,               "!box",               "val -> void",                     SIP_RECORD_END },
    { SPACES_BIP,            "!spaces",            "val -> void",                     SIP_RECORD_END },

    { IF_BIP,                "!if",                "val code -> void",                SIP_RECORD_END },
    { IFDEBUG_BIP,           "!ifdebug",           "code -> void",                    SIP_RECORD_END },
    { IFSTRICT_BIP,          "!ifstrict",          "code -> void",                    SIP_RECORD_END },
    { IFELSE_BIP,            "!ifelse",            "val code code -> void",           SIP_RECORD_END },
    { WHILE_BIP,             "!while",             "val code -> void",                SIP_RECORD_END },
    { DO_BIP,                "!do",                "val code -> void",                SIP_RECORD_END },
    { FOR_BIP,               "!for",               "val val val code -> void",        SIP_RECORD_END },
    { OBJECTLOOP_BIP,        "!objectloop",        "ref val val code -> void",        SIP_RECORD_END },
    { OBJECTLOOPX_BIP,       "!objectloopx",       "ref val code -> void",            SIP_RECORD_END },
    { BREAK_BIP,             "!break",             "void -> void",                    SIP_RECORD_END },
    { CONTINUE_BIP,          "!continue",          "void -> void",                    SIP_RECORD_END },
    { SWITCH_BIP,            "!switch",            "val code -> void",                SIP_RECORD_END },
    { CASE_BIP,              "!case",              "val code -> void",                SIP_RECORD_END },
    { ALTERNATIVECASE_BIP,   "!alternativecase",   "val val -> val",                  SIP_RECORD_END },
    { DEFAULT_BIP,           "!default",           "code -> void",                    SIP_RECORD_END },
    { RETURN_BIP,            "!return",            "val -> void",                     SIP_RECORD_END },
    { JUMP_BIP,              "!jump",              "lab -> void",                     SIP_RECORD_END },
    { QUIT_BIP,              "!quit",              "void -> void",                    SIP_RECORD_END },
    { RESTORE_BIP,           "!restore",           "lab -> void",                     SIP_RECORD_END },

    { INDIRECT0V_BIP,        "!indirect0v",        "val -> void",                     SIP_RECORD_END },
    { INDIRECT1V_BIP,        "!indirect1v",        "val val -> void",                 SIP_RECORD_END },
    { INDIRECT2V_BIP,        "!indirect2v",        "val val val -> void",             SIP_RECORD_END },
    { INDIRECT3V_BIP,        "!indirect3v",        "val val val val -> void",         SIP_RECORD_END },
    { INDIRECT4V_BIP,        "!indirect4v",        "val val val val val -> void",     SIP_RECORD_END },
    { INDIRECT5V_BIP,        "!indirect5v",        "val val val val val val -> void", SIP_RECORD_END },
    { INDIRECT6V_BIP,        "!indirect6v",        "val val val val val val val -> void", SIP_RECORD_END },
    { INDIRECT0_BIP,         "!indirect0",         "val -> val",                      SIP_RECORD_END },
    { INDIRECT1_BIP,         "!indirect1",         "val val -> val",                  SIP_RECORD_END },
    { INDIRECT2_BIP,         "!indirect2",         "val val val -> val",              SIP_RECORD_END },
    { INDIRECT3_BIP,         "!indirect3",         "val val val val -> val",          SIP_RECORD_END },
    { INDIRECT4_BIP,         "!indirect4",         "val val val val val -> val",      SIP_RECORD_END },
    { INDIRECT5_BIP,         "!indirect5",         "val val val val val val -> val",  SIP_RECORD_END },
    { INDIRECT6_BIP,         "!indirect6",         "val val val val val val val -> val",  SIP_RECORD_END },
    { MESSAGE0_BIP,          "!message0",          "val val -> val",                  SIP_RECORD_END },
    { MESSAGE1_BIP,          "!message1",          "val val val -> val",              SIP_RECORD_END },
    { MESSAGE2_BIP,          "!message2",          "val val val val -> val",          SIP_RECORD_END },
    { MESSAGE3_BIP,          "!message3",          "val val val val val -> val",      SIP_RECORD_END },
    { EXTERNALCALL_BIP,      "!externalcall",      "val val -> val",                  SIP_RECORD_END },

    { 0,                     "",                   "",                                SIP_RECORD_END }
};

§4. The following must be called before the above array can be used. It checks that the numbering is right, and converts the names and signatures from char * to text_stream *.

int standard_inform7_primitives_prepared = FALSE;
inter_ti standard_inform7_primitives_extent = 0;

void Primitives::prepare_standard_set_array(void) {
    if (standard_inform7_primitives_prepared == FALSE) {
        standard_inform7_primitives_prepared = TRUE;
        for (inter_ti i=0; ; i++) {
            if (standard_inform7_primitives[i].BIP == 0) break;
            if (i >= MAX_BIPS) internal_error("MAX_BIPS set too low");
            if (standard_inform7_primitives[i].BIP != i+1)
                internal_error("primitives table disordered");
            standard_inform7_primitives[i].name = Str::new();
            WRITE_TO(standard_inform7_primitives[i].name, "%s",
                standard_inform7_primitives[i].name_c);
            standard_inform7_primitives[i].signature = Str::new();
            WRITE_TO(standard_inform7_primitives[i].signature, "%s",
                standard_inform7_primitives[i].signature_c);
            Parse and sanity-check the signature text4.1;
            standard_inform7_primitives_extent++;
        }
    }
}

§4.1. We only care here abput the part of the signature before the ->, but we go ahead and perform a full sanity check on it anyway, just in case somebody some day amends the above table but gets it wrong. The time consumed by these checks is trivial, since this happens only once per run.

Parse and sanity-check the signature text4.1 =

    int p = 0, before_arrow = TRUE, pre_void = FALSE, inputs = 0, outputs = 0;
    text_stream *S = standard_inform7_primitives[i].signature;
    TEMPORARY_TEXT(term)
    while (p <= Str::len(S)) {
        inchar32_t c = Str::get_at(S, p);
        if ((Characters::is_whitespace(c)) || (c == 0)) {
            if ((Str::eq(term, I"->")) && (before_arrow)) before_arrow = FALSE;
            else {
                int this = -1;
                if (Str::eq(term, I"void")) {
                    this = -2;
                    if (before_arrow) pre_void = TRUE;
                }
                if (Str::eq(term, I"val"))  this = VAL_PRIM_CAT;
                if (Str::eq(term, I"ref"))  this = REF_PRIM_CAT;
                if (Str::eq(term, I"lab"))  this = LAB_PRIM_CAT;
                if (Str::eq(term, I"code")) this = CODE_PRIM_CAT;
                if (this == -1) internal_error("unknown term category in primitive");
                if (before_arrow) {
                    if (this != -2) {
                        if (inputs >= 8) internal_error("too many terms in primitive");
                        standard_inform7_primitives[i].term_categories[inputs] = this;
                        inputs++;
                    }
                } else {
                    if ((this != -2) && (this != VAL_PRIM_CAT))
                        internal_error("bad term after -> in primitive");
                    outputs++;
                }
            }
            Str::clear(term);
        } else {
            PUT_TO(term, c);
        }
        p++;
    }
    DISCARD_TEXT(term)
    if ((outputs > 1) || ((pre_void) && (inputs > 0)))
        internal_error("malformed signature in primitive");
    standard_inform7_primitives[i].term_count = inputs;
    standard_inform7_primitives[i].takes_code_blocks = FALSE;
    for (int t=0; t<inputs; t++)
        if (standard_inform7_primitives[i].term_categories[t] == CODE_PRIM_CAT)
            standard_inform7_primitives[i].takes_code_blocks = TRUE;

§5. We can now convert between BIP and name. Note that Primitives::name_to_BIP is relatively slow: but this doesn't matter because we will always cache the results (see below).

text_stream *Primitives::BIP_to_name(inter_ti bip) {
    Primitives::prepare_standard_set_array();
    if ((bip >= 1) && (bip <= standard_inform7_primitives_extent))
        return standard_inform7_primitives[bip - 1].name;
    return I"<none>";
}

inter_ti Primitives::name_to_BIP(text_stream *name) {
    Primitives::prepare_standard_set_array();
    for (inter_ti i=0; i<standard_inform7_primitives_extent; i++)
        if (Str::eq(name, standard_inform7_primitives[i].name))
            return i+1;
    return 0;
}

§6. This is printed when inter is run with the -primitives switch.

void Primitives::show_primitives(OUTPUT_STREAM) {
    WRITE("  Code     Primitive           Signature\n");
    Primitives::prepare_standard_set_array();
    for (inter_ti i=0; i<standard_inform7_primitives_extent; i++) {
        inform7_primitive *prim = &(standard_inform7_primitives[i]);
        WRITE("  %4x     %S", prim->BIP, prim->name);
        for (int j = Str::len(prim->name); j<20; j++) PUT(' ');
        WRITE("%S\n", prim->signature);
    }
}

§7. In general the standard set is a miscellany, but with one systematic family of primitives for making indirect function calls (that is, calling a function whose identity is not known at compile time). These 12 primitives all do the same thing, but vary in their signatures, according to how many arguments the function call has, and whether its return value is to be used or discarded.

The following functions allow us to ask for the right primitive for the job:

int Primitives::arity_too_great_for_indirection(int arity) {
    if (arity > 6) return TRUE;
    return FALSE;
}

inter_ti Primitives::BIP_for_indirect_call_returning_value(int arity) {
    switch (arity) {
        case 0: return INDIRECT0_BIP;
        case 1: return INDIRECT1_BIP;
        case 2: return INDIRECT2_BIP;
        case 3: return INDIRECT3_BIP;
        case 4: return INDIRECT4_BIP;
        case 5: return INDIRECT5_BIP;
        case 6: return INDIRECT6_BIP;
        default: internal_error("indirect function call with too many arguments");
    }
    return 0;
}

inter_ti Primitives::BIP_for_void_indirect_call(int arity) {
    switch (arity) {
        case 0: return INDIRECT0V_BIP;
        case 1: return INDIRECT1V_BIP;
        case 2: return INDIRECT2V_BIP;
        case 3: return INDIRECT3V_BIP;
        case 4: return INDIRECT4V_BIP;
        case 5: return INDIRECT5V_BIP;
        case 6: return INDIRECT6V_BIP;
        default: internal_error("indirectv function call with too many arguments");
    }
    return 0;
}

inter_ti Primitives::BIP_for_message_send(int arity) {
    switch (arity) {
        case 2: return MESSAGE0_BIP;
        case 3: return MESSAGE1_BIP;
        case 4: return MESSAGE2_BIP;
        case 5: return MESSAGE3_BIP;
        default: internal_error("message call must have arity 2 to 5");
    }
    return 0;
}

§8. And these functions say whether or not a BIP belongs to the family:

int Primitives::is_BIP_for_indirect_call_returning_value(inter_ti s) {
    if (s == INDIRECT0_BIP) return TRUE;
    if (s == INDIRECT1_BIP) return TRUE;
    if (s == INDIRECT2_BIP) return TRUE;
    if (s == INDIRECT3_BIP) return TRUE;
    if (s == INDIRECT4_BIP) return TRUE;
    if (s == INDIRECT5_BIP) return TRUE;
    if (s == INDIRECT6_BIP) return TRUE;
    return FALSE;
}

int Primitives::is_BIP_for_void_indirect_call(inter_ti s) {
    if (s == INDIRECT0V_BIP) return TRUE;
    if (s == INDIRECT1V_BIP) return TRUE;
    if (s == INDIRECT2V_BIP) return TRUE;
    if (s == INDIRECT3V_BIP) return TRUE;
    if (s == INDIRECT4V_BIP) return TRUE;
    if (s == INDIRECT5V_BIP) return TRUE;
    if (s == INDIRECT6V_BIP) return TRUE;
    return FALSE;
}

§9. About the terms. For example, 0 for the signature void -> val, or 2 for ref val -> val.

The *_XBIP operations are treated as if they had the signature val val -> void.

int Primitives::term_count(inter_ti BIP) {
    Primitives::prepare_standard_set_array();
    if (BIP >= LOWEST_XBIP_VALUE) return 2;
    return standard_inform7_primitives[BIP - 1].term_count;
}

§10. And this returns the primitive category for each term, counting from 0: this will be VAL_PRIM_CAT, CODE_PRIM_CAT, REF_PRIM_CAT or LAB_PRIM_CAT.

Again, the *_XBIP operations are treated as if val val -> void.

int Primitives::term_category(inter_ti BIP, int i) {
    Primitives::prepare_standard_set_array();
    if ((i < 0) || (i >= 8)) internal_error("term out of range");
    if (BIP >= LOWEST_XBIP_VALUE) return VAL_PRIM_CAT;
    return standard_inform7_primitives[BIP - 1].term_categories[i];
}

§11. Returns TRUE if any of those categories is a CODE_PRIM_CAT; note that this is cached for speed.

int Primitives::takes_code_blocks(inter_ti BIP) {
    Primitives::prepare_standard_set_array();
    if (BIP >= LOWEST_XBIP_VALUE) return FALSE;
    return standard_inform7_primitives[BIP - 1].takes_code_blocks;
}

§12. Primitives within a specific tree. So much for discussing the instruction set in the abstract: now we need code to handle its declaration in each Inter tree we make. Note that, for speed, each inter_tree structure contains the following index array inside it:

typedef struct site_primitives_data {
    struct inter_symbol *primitives_by_BIP[MAX_BIPS];
} site_primitives_data;

void Primitives::clear_site_data(inter_tree *I) {
    building_site *B = &(I->site);
    for (int i=0; i<MAX_BIPS; i++) B->spridata.primitives_by_BIP[i] = NULL;
}

§13. That array will allow us to obtain almost instantly the Inter symbol for the primitive in I having any given BIP. We need to remember that primitives can come into being in two ways, though: either by us creating them here (see below), or by Inter code being read in from an external file. If the latter, the following function must be run to make sure the index is built:

void Primitives::index_primitives_in_tree(inter_tree *I) {
    InterTree::traverse_root_only(I, Primitives::scan_visitor, NULL, PRIMITIVE_IST);
}

void Primitives::scan_visitor(inter_tree *I, inter_tree_node *P, void *v_state) {
    inter_symbol *prim = PrimitiveInstruction::primitive(P);
    inter_ti bip = Primitives::to_BIP(I, prim);
    if (bip) I->site.spridata.primitives_by_BIP[bip] = prim;
}

§14. Here is where we declare primitives. Since there are only around 100 of these, it's fine for the actual primitive declarations to be made a little slowly: so we do it by writing the declarations out in textual Inter and then parsing them. We then make various paranoid consistency checks.

void Primitives::declare_standard_set(inter_tree *I, inter_bookmark *IBM) {
    Primitives::prepare_standard_set_array();
    for (inter_ti i=0; i<standard_inform7_primitives_extent; i++)
        Primitives::declare_one(I, IBM, &(standard_inform7_primitives[i]));
}

inter_symbol *Primitives::declare_one(inter_tree *I, inter_bookmark *IBM, inform7_primitive *prim) {
    text_stream *name = prim->name;
    text_stream *signature = prim->signature;
    TEMPORARY_TEXT(prim_command)
    WRITE_TO(prim_command, "primitive %S %S", name, signature);
    Produce::guard(TextualInter::parse_single_line(prim_command, NULL, IBM));
    inter_error_message *E = NULL;
    inter_symbol *S = TextualInter::find_global_symbol(IBM, NULL, name, PRIMITIVE_IST, &E);
    inter_ti bip = Primitives::to_BIP(I, S);
    if (bip == 0) internal_error("missing bip");
    if (bip != prim->BIP) internal_error("wrong BIP");
    if (bip >= MAX_BIPS) internal_error("unsafely high bip");
    I->site.spridata.primitives_by_BIP[bip] = S;
    Produce::guard(E);
    DISCARD_TEXT(prim_command)
    return S;
}

§15. Used when parsing textual Inter:

inter_symbol *Primitives::declare_one_named(inter_tree *I, inter_bookmark *IBM,
    text_stream *name) {
    Primitives::prepare_standard_set_array();
    for (inter_ti i=0; i<standard_inform7_primitives_extent; i++) {
        inform7_primitive *prim = &(standard_inform7_primitives[i]);
        if (Str::eq(prim->name, name)) return Primitives::declare_one(I, IBM, prim);
    }
    return NULL;
}

§16. Finally, then, we provide functions to convert between BIPs and local primitive symbols.

inter_symbol *Primitives::from_BIP(inter_tree *I, inter_ti bip) {
    if (I == NULL) internal_error("no tree");
    if ((bip < 1) || (bip >= MAX_BIPS)) internal_error("bip out of range");
    inter_symbol *prim = I->site.spridata.primitives_by_BIP[bip];
    if (prim == NULL) {
        WRITE_TO(STDERR, "BIP = %d\n", bip);
        internal_error("undefined primitive");
    }
    return prim;
}

inter_ti Primitives::to_BIP(inter_tree *I, inter_symbol *symb) {
    if (symb == NULL) return 0;
    inter_ti B = PrimitiveInstruction::get_BIP(symb);
    if (B == 0) {
        B = Primitives::name_to_BIP(InterSymbol::identifier(symb));
        if (B != 0) PrimitiveInstruction::set_BIP(symb, B);
    }
    return B;
}