Storage locations into which rvalues can be put at run-time.


§1. Creation. "Lvalues" can occur on the left of an assignment sign: they are values which can be written to.

LOCAL_VARIABLE_NT refers to a specific local variable, so it has meaning only within the routine currently being compiled. A local_variable pointer is attached. There are no references or arguments.

NONLOCAL_VARIABLE_NT refers to a variable of any other scope: that is, a global variable, or perhaps a rulebook, action or activity variable. The important distinction between these other scopes and local scope is essentially that local variables live on the I6 call-stack and have only a local namespace, whereas others correspond to array entries or global I6 variables and share a global namespace. (It is basically a matter of implementation convenience which makes us divide the stock of variables into two different species this way.) A instance * pointer is attached, identifying the name of the variable in question. There are no arguments.

PROPERTY_VALUE_NT represents a given (value-)property of a given object, not the name of a property in abstract. Thus "description of the Police Commissioner" qualifies, but "description" does not. There are two arguments: the property and the object which possesses it, respectively.

TABLE_ENTRY_NT represents a given entry to a table, which can be referred to in several different ways. There are four different kinds of table reference, distinguished by the number of arguments found:

LIST_ENTRY_NT represents a given entry in a list, which is much simpler: there are two arguments, the list and the numerical index, which counts from 1.

Note that property names, table names, and lists themselves are not storage items as such — they are places where storage items are found. They are all in the VALUE family.

§2. And here are some convenient creators. Variables:

parse_node *Lvalues::new_LOCAL_VARIABLE(wording W, local_variable *lvar) {
    parse_node *spec = Node::new(LOCAL_VARIABLE_NT);
    Node::set_text(spec, W);
    Node::set_constant_local_variable(spec, lvar);
    if (lvar == NULL) internal_error("bad local variable");
    return spec;
}

parse_node *Lvalues::new_actual_NONLOCAL_VARIABLE(nonlocal_variable *nlv) {
    parse_node *spec = Node::new(NONLOCAL_VARIABLE_NT);
    Node::set_constant_nonlocal_variable(spec, nlv);
    Node::set_text(spec, nlv->name);
    return spec;
}

§3. On which subject:

local_variable *Lvalues::get_local_variable_if_any(parse_node *spec) {
    if (Node::is(spec, LOCAL_VARIABLE_NT))
        return Node::get_constant_local_variable(spec);
    return NULL;
}

§4. Table entries have their arguments filled in by the relevant routines in "Meaning List Conversion":

parse_node *Lvalues::new_TABLE_ENTRY(wording W) {
    parse_node *spec = Node::new_with_words(TABLE_ENTRY_NT, W);
    return spec;
}

§5. List entries:

parse_node *Lvalues::new_LIST_ENTRY(parse_node *owner, parse_node *index) {
    parse_node *spec = Node::new(LIST_ENTRY_NT);
    spec->down = owner;
    spec->down->next = index;
    return spec;
}

§6. Property values are constructed out of what's often only implied text: for instance, "description" sometimes means "the description [of the self object]". We give them a word range which is minimal such that it must contain word ranges of both property and owner, if given. Thus "carrying capacity of the trunk" will result from "carrying capacity" and "trunk". This is not very scientific, perhaps, but it's done only to make problem messages more readable.

parse_node *Lvalues::new_PROPERTY_VALUE(parse_node *prop, parse_node *owner) {
    parse_node *spec = Node::new(PROPERTY_VALUE_NT);
    spec->down = prop;
    spec->down->next = owner;
    Node::set_text(spec,
        Wordings::union(Node::get_text(prop), Node::get_text(owner)));
    return spec;
}

§7. On the other hand we sometimes want to refer to the property in abstract.

parse_node *Lvalues::underlying_property(parse_node *spec) {
    if (Node::is(spec, PROPERTY_VALUE_NT)) {
        if (Rvalues::is_self_object_constant(spec->down->next))
            return spec->down;
        return spec;
    }
    internal_error("no underlying property"); return NULL;
}

§8. Testing.

int Lvalues::is_lvalue(parse_node *spec) {
    node_type_metadata *metadata = NodeType::get_metadata(Node::get_type(spec));
    if ((metadata) && (metadata->category == LVALUE_NCAT)) return TRUE;
    return FALSE;
}

node_type_t Lvalues::get_storage_form(parse_node *spec) {
    if (Lvalues::is_lvalue(spec)) return Node::get_type(spec);
    return UNKNOWN_NT;
}

§9. More specifically:

int Lvalues::is_actual_NONLOCAL_VARIABLE(parse_node *spec) {
    if (Node::is(spec, NONLOCAL_VARIABLE_NT)) return TRUE;
    return FALSE;
}

nonlocal_variable *Lvalues::get_nonlocal_variable_if_any(parse_node *spec) {
    if (Node::is(spec, NONLOCAL_VARIABLE_NT))
        return Node::get_constant_nonlocal_variable(spec);
    return NULL;
}

int Lvalues::is_constant_NONLOCAL_VARIABLE(parse_node *spec) {
    nonlocal_variable *nlv = Lvalues::get_nonlocal_variable_if_any(spec);
    if (nlv) return NonlocalVariables::is_constant(nlv);
    return FALSE;
}

§10. Not all non-local variables are global — some have scope local to rulebooks, actions and the like:

int Lvalues::is_global_variable(parse_node *spec) {
    if (Lvalues::get_nonlocal_variable_if_any(spec)) return TRUE;
    return FALSE;
}

§11. Pretty-printing.

void Lvalues::write_out_in_English(OUTPUT_STREAM, parse_node *spec) {
    switch(Node::get_type(spec)) {
        case LOCAL_VARIABLE_NT: WRITE("a temporary named value"); break;
        case NONLOCAL_VARIABLE_NT:
            if (Node::get_kind_of_value(spec)) {
                Kinds::Textual::write_articled(OUT, Node::get_kind_of_value(spec));
                WRITE(" that varies");
            } else WRITE("a non-temporary variable");
            break;
        case TABLE_ENTRY_NT: WRITE("a table entry"); break;
        case LIST_ENTRY_NT: WRITE("a list entry"); break;
        case PROPERTY_VALUE_NT:
            if ((Node::no_children(spec) == 2) &&
                (Rvalues::is_CONSTANT_construction(spec->down, CON_property))) {
                property *prn = Rvalues::to_property(
                    spec->down);
                WRITE("a property whose value is ");
                Kinds::Textual::write_articled(OUT, ValueProperties::kind(prn));
            } else WRITE("a property belonging to something");
            break;
        default: WRITE("a stored value"); break;
    }
}

§12. Kinds.

kind *Lvalues::to_kind(parse_node *spec) {
    if (spec == NULL) internal_error("Rvalues::to_kind on NULL");
    switch (Node::get_type(spec)) {
        case LOCAL_VARIABLE_NT: Return the kind of a local variable12.1;
        case NONLOCAL_VARIABLE_NT: Return the kind of a non-local variable12.2;
        case TABLE_ENTRY_NT: Return the kind of a table entry12.3;
        case LIST_ENTRY_NT: Return the kind of a list entry12.4;
        case PROPERTY_VALUE_NT: Return the kind of a property value12.5;
    }
    return K_value;  a generic answer for storage of an unknown sort
}

§12.1. Return the kind of a local variable12.1 =

    local_variable *lvar = Node::get_constant_local_variable(spec);
    if (lvar == NULL) return K_value;  for "existing"
    return LocalVariables::kind(lvar);

§12.2. Return the kind of a non-local variable12.2 =

    nonlocal_variable *nlv = Node::get_constant_nonlocal_variable(spec);
    return NonlocalVariables::kind(nlv);

§12.3. In every form of table entry, argument 0 is the column, and the column is enough to determine the kind:

Return the kind of a table entry12.3 =

    if (Node::no_children(spec) > 0) {  i.e., always, for actual table entry specifications
        parse_node *fts = spec->down;
        table_column *tc = Rvalues::to_table_column(fts);
        return Tables::Columns::get_kind(tc);
    }
    return NULL;  can happen when scanning phrase arguments, which are generic

§12.4. Return the kind of a list entry12.4 =

    if (Node::no_children(spec) == 2) {  i.e., always, for actual list entry specifications
        kind *K1 = Specifications::to_kind(spec->down);
        if (Kinds::unary_construction_material(K1)) return Kinds::unary_construction_material(K1);
        return K_value;  to help the type-checker produce better problem messages
    }
    return NULL;  can happen when scanning phrase arguments, which are generic

§12.5. Return the kind of a property value12.5 =

    if (Node::no_children(spec) == 2) {
        property *prn = Rvalues::to_property(spec->down);
        if ((prn) && (Properties::is_either_or(prn) == FALSE)) return ValueProperties::kind(prn);
        return K_value;  to help the type-checker produce better problem messages
    }
    return NULL;  can happen when scanning phrase arguments, which are generic