Variables shared by the rules of the rulebooks processing an action.

§1. Action names, that is, instances of K_action_name, do not have properties. Instead, sentences which look as if they will assign properties are turned into creations of action variables:

int ActionVariables::actions_offered_property(kind *K, parse_node *owner, parse_node *prop) {
    if (Kinds::eq(K, K_action_name)) {
        action_name *an = ARvalues::to_action_name(owner);
        if (an == NULL) internal_error("failed to extract action-name structure");
        if (global_pass_state.pass == 1) {
            Require the variable to have an explicit name1.1;
            kind *K = NULL;
            Determine and vet the kind of the new variable1.2;
            wording NW = Node::get_text(prop->down->next);
            wording MW = EMPTY_WORDING;
            Find the name and match wordings1.3;
            Reject multi-word match wordings1.4;
            ActionVariables::new(an, K, NW, MW);
        }
        return TRUE;
    }
    return FALSE;
}

§1.1. Properties can be nameless, or rather, have the name only of their kind; but action variables cannot.

Require the variable to have an explicit name1.1 =

    if (Node::get_type(prop) != PROPERTYCALLED_NT) {
        Problems::quote_source(1, current_sentence);
        StandardProblems::handmade_problem(Task::syntax_tree(),
            _p_(PM_ActionVarUncalled));
        Problems::issue_problem_segment(
            "You wrote %1, which I am reading as a request to make a new named "
            "variable for an action - a value associated with a action and which "
            "has a name. But since you only give a kind, not a name, I'm stuck. "
            "('The taking action has a number called tenacity' is right, 'The "
            "taking action has a number' is too vague.)");
        Problems::issue_problem_end();
        return TRUE;
    }

§1.2. Determine and vet the kind of the new variable1.2 =

    wording KW = Node::get_text(prop->down);
    if (<k-kind>(KW)) K = <<rp>>;
    else {
        parse_node *spec = NULL;
        if (<s-type-expression>(KW)) spec = <<rp>>;
        if (Specifications::is_description(spec)) {
            Problems::quote_source(1, current_sentence);
            Problems::quote_wording(2, KW);
            StandardProblems::handmade_problem(Task::syntax_tree(),
                _p_(PM_ActionVarOverspecific));
            Problems::issue_problem_segment(
                "You wrote %1, which I am reading as a request to make a new named "
                "variable for an action - a value associated with a action and which "
                "has a name. The request seems to say that the value in question is "
                "'%2', but this is too specific a description. (Instead, a kind of "
                "value (such as 'number') or a kind of object (such as 'room' or "
                "'thing') should be given. To get a property whose contents can be "
                "any kind of object, use 'object'.)");
            Problems::issue_problem_end();
        } else {
            LOG("Offending SP: $T", spec);
            Problems::quote_source(1, current_sentence);
            Problems::quote_wording(2, KW);
            StandardProblems::handmade_problem(Task::syntax_tree(),
                _p_(PM_ActionVarUnknownKOV));
            Problems::issue_problem_segment(
                "You wrote %1, but '%2' is not the name of a kind of value which "
                "I know (such as 'number' or 'text').");
            Problems::issue_problem_end();
        }
        return TRUE;
    }

    if (Kinds::Behaviour::definite(K) == FALSE) {
        Problems::quote_source(1, current_sentence);
        Problems::quote_wording(2, KW);
        StandardProblems::handmade_problem(Task::syntax_tree(),
            _p_(PM_ActionVarValue));
        Problems::issue_problem_segment(
            "You wrote %1, but saying that a variable is a 'value' does not give me "
            "a clear enough idea what it will hold. You need to say what kind of "
            "value: for instance, 'A door has a number called street address.' is "
            "allowed because 'number' is specific about the kind of value.");
        Problems::issue_problem_end();
        return TRUE;
    }

§2. Variable names need to be sensible, but can have bracketed match clauses:

<action-variable> ::=
    <action-variable-name> ( matched as {<quoted-text-without-subs>} ) | ==> { TRUE, - }
    <action-variable-name> ( ... ) |          ==> Issue PM_BadMatchingSyntax problem2.1
    <action-variable-name>                    ==> { FALSE, - }

<action-variable-name> ::=
    <unfortunate-name> |                      ==> Issue PM_ActionVarAnd problem2.2
    ...                                       ==> { TRUE, - }

§2.1. Issue PM_BadMatchingSyntax problem2.1 =

    Problems::quote_source(1, current_sentence);
    Problems::quote_wording(2, W);
    StandardProblems::handmade_problem(Task::syntax_tree(),
        _p_(PM_BadMatchingSyntax));
    Problems::issue_problem_segment(
        "You wrote %1, which I am reading as a request to make "
        "a new named variable for an action - a value associated "
        "with a action and which has a name. The request seems to "
        "say in parentheses that the name in question is '%2', but "
        "I only recognise the form '(matched as \"some text\")' here.");
    Problems::issue_problem_end();
    ==> { NOT_APPLICABLE, - };

§2.2. Issue PM_ActionVarAnd problem2.2 =

    Problems::quote_source(1, current_sentence);
    Problems::quote_wording(2, W);
    StandardProblems::handmade_problem(Task::syntax_tree(),
        _p_(PM_ActionVarAnd));
    Problems::issue_problem_segment(
        "You wrote %1, which I am reading as a request to make "
        "a new named variable for an action - a value associated "
        "with a action and which has a name. The request seems to "
        "say that the name in question is '%2', but I'd prefer to "
        "avoid 'and', 'or', 'with', or 'having' in such names, please.");
    Problems::issue_problem_end();
    ==> { NOT_APPLICABLE, - };

§1.3. Find the name and match wordings1.3 =

    if (<action-variable>(NW)) {
        if (<<r>> == NOT_APPLICABLE) return TRUE;
        NW = GET_RW(<action-variable-name>, 1);
        if (<<r>>) {
            MW = GET_RW(<action-variable>, 1);
            int wn = Wordings::first_wn(MW);
            Word::dequote(wn);
            MW = Feeds::feed_C_string(Lexer::word_text(wn));
        }
    }

§1.4. Reject multi-word match wordings1.4 =

    if (Wordings::length(MW) > 1) {
        Problems::quote_source(1, current_sentence);
        Problems::quote_wording(2, MW);
        StandardProblems::handmade_problem(Task::syntax_tree(),
            _p_(PM_MatchedAsTooLong));
        Problems::issue_problem_segment(
            "You wrote %1, which I am reading as a request to make "
            "a new named variable for an action - a value associated "
            "with a action and which has a name. You say that it should "
            "be '(matched as \"%2\")', but I can only recognise such "
            "matches when a single keyword is used to introduce the "
            "clause, and this is more than one word.");
        Problems::issue_problem_end();
        return TRUE;
    }

§3. An owner list is maintained for all sets of actions which actually have variables (hence "nonempty") — many will not, in practice:

shared_variable_access_list *all_nonempty_stacked_action_vars = NULL;

§4.

void ActionVariables::new(action_name *an, kind *K, wording NW, wording MW) {
    if (all_nonempty_stacked_action_vars == NULL)
        all_nonempty_stacked_action_vars = SharedVariables::new_access_list();
    if (SharedVariables::set_empty(an->action_variables))
        SharedVariables::add_set_to_access_list(
            all_nonempty_stacked_action_vars, an->action_variables);

    shared_variable *stv = SharedVariables::new(an->action_variables, NW, K, FALSE);
    if (Wordings::nonempty(MW))
        SharedVariables::set_matching_text(stv, MW);

    LOGIF(ACTION_CREATIONS, "Created action variable for $l: %W (%u)\n", an, NW, K);
    if (Wordings::nonempty(MW))
        LOGIF(ACTION_CREATIONS, "Match with text: %W + SP\n", MW);
}

§5. Action variables can optionally be marked as able to extend the grammar of action patterns. For example, the Standard Rules define:

The exiting action has an object called the container exited from (matched as "from").

and this allows "exiting from the cage", say, as an action pattern.

shared_variable *ActionVariables::parse_match_clause(action_name *an, wording W) {
    return SharedVariables::parse_match_clause(an->action_variables, W);
}