Shared variables are held in common by all rules working in some goal.


§1. Introduction. Inform allows some variables to be shared by a number of different rules (with different stack frames) and yet not be global in scope or permanent in existence: they are shared by some process carried out by rulebooks.

The semantics of shared variables are unusual because rules can be in multiple rulebooks, or can be moved out of their expected rulebooks. Their names are therefore not limited in scope — they are global, and they belong to the same namespace as global variables. But access to them is restricted to just those rules with permission.

Each shared_variable belongs to just one shared_variable_set, but the code forming the body of a rule may be able to access multiple sets. So each stack frame has its own shared_variable_access_list of those sets which it can see.

§2. Variables. As can be seen, a shared variable is really just some additional expectations placed on a nonlocal variable (for which, see Nonlocal Variables (in knowledge)):

typedef struct shared_variable {
    struct shared_variable_set *owner;  who owns this
    int index_in_owner;  counting upwards from 0
    struct wording name;  text of the name
    struct parse_node *assigned_at;  sentence creating the variable
    struct nonlocal_variable *underlying_var;  the variable in question
    struct wording match_wording_text;  matching text (relevant for action variables only)
    CLASS_DEFINITION
} shared_variable;

§3. And it can only be created within a set:

shared_variable *SharedVariables::new(shared_variable_set *set, wording W, kind *K) {
    shared_variable *shv = CREATE(shared_variable);
    W = Articles::remove_the(W);
    shv->name = W;
    shv->owner = set;
    shv->index_in_owner = LinkedLists::len(set->variables);
    shv->assigned_at = current_sentence;
    shv->match_wording_text = EMPTY_WORDING;
    nonlocal_variable *nlv = NonlocalVariables::new(W, K, shv);
    shv->underlying_var = nlv;
    RTVariables::set_I6_identifier(nlv, FALSE, RTVariables::shv_rvalue(shv));
    RTVariables::set_I6_identifier(nlv, TRUE, RTVariables::shv_lvalue(shv));
    SharedVariables::add_to_set(shv, set);
    return shv;
}

§4. Some miscellaneous access functions:

int SharedVariables::get_owner_id(shared_variable *shv) {
    return shv->owner->recognition_id;
}

inter_name *SharedVariables::get_owner_iname(shared_variable *shv) {
    return shv->owner->recognition_iname;
}

int SharedVariables::get_index(shared_variable *shv) {
    return shv->index_in_owner;
}

kind *SharedVariables::get_kind(shared_variable *shv) {
    nonlocal_variable *nlv = SharedVariables::get_variable(shv);
    return NonlocalVariables::kind(nlv);
}

nonlocal_variable *SharedVariables::get_variable(shared_variable *shv) {
    if (shv == NULL) return NULL;
    return shv->underlying_var;
}

§5. The match text associated with a shared variable is used in parsing action patterns: see Action Name Lists (in if). But for most shared variables, this text remains empty.

void SharedVariables::set_matching_text(shared_variable *shv, wording W) {
    shv->match_wording_text = W;
}

wording SharedVariables::get_matching_text(shared_variable *shv) {
    return shv->match_wording_text;
}

§6. Sets. Sets are identified at run-time by an ID number, the "recognition ID", which must be unique to that set and also small enough to be stored in what might only be a 16-bit unsigned integer.

typedef struct shared_variable_set {
    int recognition_id;
    struct inter_name *recognition_iname;
    struct linked_list *variables;  of shared_variable
    struct shared_variable_set_compilation_data compilation_data;
    CLASS_DEFINITION
} shared_variable_set;

shared_variable_set *SharedVariables::new_set(int id, inter_name *iname) {
    shared_variable_set *set = CREATE(shared_variable_set);
    set->recognition_id = id;
    set->recognition_iname = iname;
    set->variables = NEW_LINKED_LIST(shared_variable);
    set->compilation_data = RTVariables::new_set_data(set);
    return set;
}

int SharedVariables::set_empty(shared_variable_set *set) {
    if (LinkedLists::len(set->variables) == 0) return TRUE;
    return FALSE;
}

int size_of_largest_set = 0;

void SharedVariables::add_to_set(shared_variable *shv, shared_variable_set *set) {
    ADD_TO_LINKED_LIST(shv, shared_variable, set->variables);
    if (LinkedLists::len(set->variables) > size_of_largest_set)
        size_of_largest_set = LinkedLists::len(set->variables);
}

int SharedVariables::size_of_largest_set(void) {
    return size_of_largest_set;
}

§7. Returns the first variable in the set whose matching text begins W. Note that this requires the match text to be nonempty, so it can only return variables which have one.

shared_variable *SharedVariables::parse_match_clause(shared_variable_set *set,
    wording W) {
    shared_variable *shv;
    LOOP_OVER_LINKED_LIST(shv, shared_variable, set->variables)
        if (Wordings::starts_with(W, shv->match_wording_text))
            return shv;
    return NULL;
}

§8. Access lists. These could hardly be simpler:

typedef struct shared_variable_access_list {
    struct linked_list *sets;  of shared_variable_set
    CLASS_DEFINITION
} shared_variable_access_list;

shared_variable_access_list *SharedVariables::new_access_list(void) {
    shared_variable_access_list *nshvol = CREATE(shared_variable_access_list);
    nshvol->sets = NEW_LINKED_LIST(shared_variable_set);
    return nshvol;
}

§9. Duplicates are not allowed:

void SharedVariables::add_set_to_access_list(shared_variable_access_list *access,
    shared_variable_set *set) {
    if (access) {
        shared_variable_set *existing;
        LOOP_OVER_LINKED_LIST(existing, shared_variable_set, access->sets)
            if (existing == set)
                return;
        ADD_TO_LINKED_LIST(set, shared_variable_set, access->sets);
    }
}

§10. This changes access to the union of access and extras:

void SharedVariables::append_access_list(shared_variable_access_list *access,
    shared_variable_access_list *extras) {
    shared_variable_set *set;
    if ((extras) && (access))
        LOOP_OVER_LINKED_LIST(set, shared_variable_set, extras->sets)
            SharedVariables::add_set_to_access_list(access, set);
}

§11. Returns the first shared variable of the given name W in any set in the access list. This would be inefficient if access lists were ever large, or if individual sets were, but they are not. Giving each access list its own associative hash would make little or no saving of time, and would decrease the predictability of results.

shared_variable *SharedVariables::parse_from_access_list(shared_variable_access_list *access,
    wording W) {
    if (Wordings::empty(W)) return NULL;
    W = Articles::remove_the(W);
    shared_variable_set *set;
    if (access)
        LOOP_OVER_LINKED_LIST(set, shared_variable_set, access->sets) {
            shared_variable *shv;
            LOOP_OVER_LINKED_LIST(shv, shared_variable, set->variables)
                if (Wordings::match(shv->name, W))
                    return shv;
        }
    return NULL;
}