diff --git a/docs/assertions-module/3-tr.html b/docs/assertions-module/3-tr.html index f669e6b15..10d43e8af 100644 --- a/docs/assertions-module/3-tr.html +++ b/docs/assertions-module/3-tr.html @@ -463,10 +463,8 @@ will be required to pass < Translations::plus_responses(p->down->next, R); } else { if (<extra-response>(Node::get_text(p))) { - int code = <<r>>; - response_message *resp = Responses::response_cue(NULL, R, - code, Node::get_text(p), NULL, TRUE); - Rules::set_response(R, code, resp); + int marker = <<r>>; + Responses::set_via_translation(R, marker, Node::get_text(p)); } else { StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_I6ResponsesAwry), diff --git a/docs/assertions-module/4-ass.html b/docs/assertions-module/4-ass.html index 6afc603aa..b469c1a45 100644 --- a/docs/assertions-module/4-ass.html +++ b/docs/assertions-module/4-ass.html @@ -2333,7 +2333,7 @@ but that's not evident without a lot of contextual checking. (Rvalues::is_CONSTANT_of_kind(val, K_text))) { rule *R = Rvalues::to_rule(constant); int c = Annotations::read_int(constant, response_code_ANNOT); - Responses::assert_response_value(R, c, Node::get_text(val)); + Rules::now_rule_needs_response(R, c, Node::get_text(val)); return; } diff --git a/docs/assertions-module/6-rls.html b/docs/assertions-module/6-rls.html index a7f85bde8..c47e128d1 100644 --- a/docs/assertions-module/6-rls.html +++ b/docs/assertions-module/6-rls.html @@ -635,7 +635,7 @@ possibility we store one of these: return rr; } -wording Rules::get_response_content(rule *R, int code) { +wording Rules::get_response_replacement_wording(rule *R, int code) { if (R == NULL) return EMPTY_WORDING; if ((code < 0) || (code >= 26)) return EMPTY_WORDING; return R->responses[code].content; @@ -653,7 +653,7 @@ created with Rules::set_response:

-void Rules::set_response(rule *R, int code, response_message *resp) {
+void Rules::set_response(rule *R, int code, response_message *resp) {
     if (R == NULL) internal_error("null rule defines response");
     if ((code < 0) || (code >= 26)) internal_error("response out of range");
     R->responses[code].message = resp;
@@ -670,7 +670,7 @@ tries to change its wording to the new text 
-void Rules::now_rule_needs_response(rule *R, int code, wording W) {
+void Rules::now_rule_needs_response(rule *R, int code, wording W) {
     if (R == NULL) internal_error("null rule uses response");
     if ((code < 0) || (code >= 26)) internal_error("response out of range");
     R->responses[code].used = current_sentence;
diff --git a/docs/core-module/1-htc.html b/docs/core-module/1-htc.html
index 1970e7e0e..f1b5f4de1 100644
--- a/docs/core-module/1-htc.html
+++ b/docs/core-module/1-htc.html
@@ -341,7 +341,7 @@ so on. Those absolute basics are made here.
     BENCH(PhraseRequests::invoke_to_begin)
     BENCH(Closures::compile_closures)
     BENCH(Sequence::compile_function_resources)
-    BENCH(Responses::compile_responses)
+    BENCH(Responses::compile_synoptic_resources)
     BENCH(Sequence::compile_literal_resources)
     BENCH(RTRelations::compile_defined_relations)
     BENCH(Sequence::compile_function_resources)
@@ -448,6 +448,7 @@ other's bodies. But I think the term "coroutine" is reasonable just the same.
         if (PhraseRequests::compilation_coroutine() > 0)       repeat = TRUE;
         if (ListTogether::compilation_coroutine() > 0)         repeat = TRUE;
         if (LoopingOverScope::compilation_coroutine() > 0)     repeat = TRUE;
+        if (Responses::compilation_coroutine() > 0)            repeat = TRUE;
         if (TextSubstitutions::compilation_coroutine() > 0)    repeat = TRUE;
         if (DeferredPropositions::compilation_coroutine() > 0) repeat = TRUE;
 
diff --git a/docs/imperative-module/2-cr.html b/docs/imperative-module/2-cr.html
index 7f9cf243f..1fce32a4a 100644
--- a/docs/imperative-module/2-cr.html
+++ b/docs/imperative-module/2-cr.html
@@ -249,7 +249,7 @@ kinds of value:
         return;
     }
     if (Kinds::eq(kind_of_constant, K_text)) {
-        Responses::compile_general(VH, value);
+        CompileRvalues::text(VH, value);
         return;
     }
     #ifdef IF_MODULE
@@ -339,6 +339,105 @@ contexts by using a tilde: ~a
     }
 
+

§2. Texts can be compiled in four different ways, so the following splits into +four cases. Note that responses take the form +

+ +
+    "blah blah blah" ( letter )
+
+

so the penultimate word, if it's there, is the letter. +

+ +
+void CompileRvalues::text(value_holster *VH, parse_node *str) {
+    if (Holsters::non_void_context(VH) == FALSE) internal_error("text in void context");
+    if (Annotations::read_int(str, explicit_literal_ANNOT)) {
+        This is an explicit text2.1;
+    } else {
+        wording SW = Node::get_text(str);
+        int unescaped = Annotations::read_int(str, text_unescaped_ANNOT);
+        if (Wordings::empty(SW)) internal_error("text without wording");
+        if ((Wordings::length(SW) >= 2) &&
+            (<response-letter>(Wordings::one_word(Wordings::last_wn(SW)-1)))) {
+            This is a response text2.2;
+        } else if ((unescaped == 0) &&
+                (Vocabulary::test_flags(Wordings::first_wn(SW), TEXTWITHSUBS_MC))) {
+            This is a text substitution2.3;
+        } else if (unescaped) {
+            This is an unescaped text literal2.4;
+        } else {
+            This is a regular text literal2.5;
+        }
+    }
+}
+
+

§2.1. Not explicit in the sense of an advisory sticker on an Eminem CD: explicit +in providing a text stream for its content, rather than a wording from the +source text. (This usually means it has been manufactured somewhere in the +compiler, rather than parsed from the source.) +

+ +

This is an explicit text2.1 = +

+ +
+    if (Node::get_explicit_iname(str)) {
+        if (Holsters::non_void_context(VH)) {
+            Emit::holster_iname(VH, Node::get_explicit_iname(str));
+        } else internal_error("unvalued SCG");
+    } else {
+        int A = Annotations::read_int(str, constant_number_ANNOT);
+        if (Holsters::non_void_context(VH))
+            Holsters::holster_pair(VH, LITERAL_IVAL, (inter_ti) A);
+    }
+
+ +

§2.2. This is a response text2.2 = +

+ +
+    int marker = <<r>>;
+    if ((rule_being_compiled == NULL) ||
+        (Rules::rule_allows_responses(rule_being_compiled) == FALSE)) {
+        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_ResponseContextWrong),
+            "lettered responses can only be used in named rules",
+            "not in any of the other contexts in which quoted text can appear.");
+        return;
+    }
+    if (Rules::get_response(rule_being_compiled, marker)) {
+        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_ResponseDuplicated),
+            "this duplicates a response letter",
+            "which is not allowed: if a bracketed letter like (A) is used to mark "
+            "some text as a response, then it can only occur once in its rule.");
+        return;
+    }
+    Responses::set_via_source_text(VH, rule_being_compiled, marker, SW);
+
+ +

§2.3. This is a text substitution2.3 = +

+ +
+    TextSubstitutions::text_substitution_cue(VH, SW);
+
+ +

§2.4. This is an unescaped text literal2.4 = +

+ +
+    inter_name *val_iname = TextLiterals::to_value_unescaped(SW);
+    Emit::holster_iname(VH, val_iname);
+
+ +

§2.5. This is a regular text literal2.5 = +

+ +
+    inter_name *val_iname = TextLiterals::to_value(SW);
+    Emit::holster_iname(VH, val_iname);
+
+ diff --git a/docs/imperative-module/4-cs.html b/docs/imperative-module/4-cs.html index 46bfdd7fc..e93d54881 100644 --- a/docs/imperative-module/4-cs.html +++ b/docs/imperative-module/4-cs.html @@ -208,7 +208,7 @@ Here this is th if ((m & ADOPT_LOCAL_STACK_FRAME_ISSBM) && (Rvalues::is_CONSTANT_of_kind(other->constant, K_response))) { rule_to_which_this_is_a_response = Rvalues::to_rule(other->constant); - response_marker_within_that_rule = Responses::get_marker_from_response_spec(other->constant); + response_marker_within_that_rule = Rvalues::to_response_marker(other->constant); } kind *K = NULL; if (m & CAST_TO_KIND_OF_OTHER_TERM_ISSBM) K = other->term_checked_as_kind; diff --git a/docs/index-module/2-rls.html b/docs/index-module/2-rls.html index de27d5273..42e0f9d8f 100644 --- a/docs/index-module/2-rls.html +++ b/docs/index-module/2-rls.html @@ -172,7 +172,7 @@ function togglePopup(material_id) { if (R->responses[l].message) { if (c == 0) Index::extra_div_open_nested(OUT, 1000000+R->allocation_id, 2); else HTML_TAG("br"); - Responses::index_response(OUT, R, l, R->responses[l].message); + IXRules::index_response(OUT, R, l, R->responses[l].message); c++; } if (c > 0) Index::extra_div_close_nested(OUT); @@ -907,6 +907,34 @@ being the relevant scene or action for a rule. } +

§12. When we index a response, we also provide a paste button for the source +text to assert a change: +

+ +
+void IXRules::index_response(OUTPUT_STREAM, rule *R, int marker, response_message *resp) {
+    WRITE("&nbsp;&nbsp;&nbsp;&nbsp;");
+    HTML_OPEN_WITH("span",
+        "style=\"color: #ffffff; "
+        "font-family: 'Courier New', Courier, monospace; background-color: #8080ff;\"");
+    WRITE("&nbsp;&nbsp;%c&nbsp;&nbsp; ", 'A' + marker);
+    HTML_CLOSE("span");
+    HTML_OPEN_WITH("span", "style=\"color: #000066;\"");
+    WRITE("%+W", resp->the_ts->unsubstituted_text);
+    HTML_CLOSE("span");
+    WRITE("&nbsp;&nbsp;");
+    TEMPORARY_TEXT(S)
+    WRITE_TO(S, "%+W response (%c)", R->name, 'A' + marker);
+    PasteButtons::paste_text(OUT, S);
+    WRITE("&nbsp;<i>name</i>");
+    WRITE("&nbsp;");
+    Str::clear(S);
+    WRITE_TO(S, "The %+W response (%c) is \"New text.\".");
+    PasteButtons::paste_text(OUT, S);
+    WRITE("&nbsp;<i>set</i>");
+    DISCARD_TEXT(S)
+}
+
diff --git a/docs/runtime-module/2-cu.html b/docs/runtime-module/2-cu.html index f7ed40733..6489eb364 100644 --- a/docs/runtime-module/2-cu.html +++ b/docs/runtime-module/2-cu.html @@ -192,7 +192,7 @@ by hand as belonging to the same nodes as their progenitors:

-void CompilationUnits::assign_to_same_unit(parse_node *to, parse_node *from) {
+void CompilationUnits::assign_to_same_unit(parse_node *to, parse_node *from) {
     CompilationUnits::join(to, Node::get_unit(from));
 }
 
diff --git a/docs/runtime-module/2-ea.html b/docs/runtime-module/2-ea.html index 0e0c4ce0b..cdaf1233c 100644 --- a/docs/runtime-module/2-ea.html +++ b/docs/runtime-module/2-ea.html @@ -92,7 +92,7 @@ enforced; it's fine to store arbitrary data with
-packaging_state EmitArrays::begin(inter_name *name, kind *K) {
+packaging_state EmitArrays::begin(inter_name *name, kind *K) {
     packaging_state save = Packaging::enter_home_of(name);
     EmitArrays::begin_inner(name, K, FALSE);
     return save;
@@ -158,11 +158,11 @@ which would be a typesafe list in I7, so they can be absolutely any data,
 

-void EmitArrays::numeric_entry(inter_ti N) {
+void EmitArrays::numeric_entry(inter_ti N) {
     EmitArrays::entry_inner(LITERAL_IVAL, N);
 }
 
-void EmitArrays::iname_entry(inter_name *iname) {
+void EmitArrays::iname_entry(inter_name *iname) {
     inter_symbol *alias;
     if (iname == NULL) alias = Emit::get_veneer_symbol(NOTHING_VSYMB);
     else alias = InterNames::to_symbol(iname);
@@ -175,7 +175,7 @@ which would be a typesafe list in I7, so they can be absolutely any data,
     EmitArrays::iname_entry(Hierarchy::find(NULL_HL));
 }
 
-void EmitArrays::text_entry(text_stream *content) {
+void EmitArrays::text_entry(text_stream *content) {
     inter_ti v1 = 0, v2 = 0;
     Produce::text_value(Emit::tree(), &v1, &v2, content);
     EmitArrays::entry_inner(v1, v2);
@@ -213,7 +213,7 @@ difference to compiled code.
 

-void EmitArrays::end(packaging_state save) {
+void EmitArrays::end(packaging_state save) {
     EmitArrays::end_inner();
     Packaging::exit(Emit::tree(), save);
 }
diff --git a/docs/runtime-module/2-ec.html b/docs/runtime-module/2-ec.html
index 1baab018b..a2f98cb3a 100644
--- a/docs/runtime-module/2-ec.html
+++ b/docs/runtime-module/2-ec.html
@@ -108,10 +108,10 @@ instruction last emitted, not after it.
 
  • EmitCode::up then returns us back to where we were.
  • -void EmitCode::up(void) {
    +void EmitCode::up(void) {
         Produce::up(Emit::tree());
     }
    -void EmitCode::down(void) {
    +void EmitCode::down(void) {
         Produce::down(Emit::tree());
     }
     
    @@ -127,7 +127,7 @@ have made, net:

    §5. Structural.

    -void EmitCode::code(void) {
    +void EmitCode::code(void) {
         Produce::code(Emit::tree());
     }
     
    @@ -153,23 +153,23 @@ start of a function.
     

    -void EmitCode::val_number(inter_ti N) {
    +void EmitCode::val_number(inter_ti N) {
         Produce::val(Emit::tree(), K_number, LITERAL_IVAL, N);
     }
     
    -void EmitCode::val_true(void) {
    +void EmitCode::val_true(void) {
         Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 1);
     }
     
    -void EmitCode::val_false(void) {
    +void EmitCode::val_false(void) {
         Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 0);
     }
     
    -void EmitCode::val_iname(kind *K, inter_name *iname) {
    +void EmitCode::val_iname(kind *K, inter_name *iname) {
         Produce::val_iname(Emit::tree(), K, iname);
     }
     
    -void EmitCode::val_text(text_stream *text) {
    +void EmitCode::val_text(text_stream *text) {
         Produce::val_text(Emit::tree(), text);
     }
     
    @@ -185,7 +185,7 @@ start of a function.
         Produce::val_nothing(Emit::tree());
     }
     
    -void EmitCode::val_symbol(kind *K, inter_symbol *S) {
    +void EmitCode::val_symbol(kind *K, inter_symbol *S) {
         Produce::val_symbol(Emit::tree(), K, S);
     }
     
    @@ -201,11 +201,11 @@ start of a function.

    -void EmitCode::ref_iname(kind *K, inter_name *iname) {
    +void EmitCode::ref_iname(kind *K, inter_name *iname) {
         Produce::ref_iname(Emit::tree(), K, iname);
     }
     
    -void EmitCode::ref_symbol(kind *K, inter_symbol *S) {
    +void EmitCode::ref_symbol(kind *K, inter_symbol *S) {
         Produce::ref_symbol(Emit::tree(), K, S);
     }
     
    @@ -213,11 +213,11 @@ start of a function.

    -void EmitCode::inv(inter_ti bip) {
    +void EmitCode::inv(inter_ti bip) {
         Produce::inv_primitive(Emit::tree(), bip);
     }
     
    -void EmitCode::call(inter_name *fn_iname) {
    +void EmitCode::call(inter_name *fn_iname) {
         Produce::inv_call_iname(Emit::tree(), fn_iname);
     }
     
    @@ -231,7 +231,7 @@ current function:
     

    -void EmitCode::rtrue(void) {
    +void EmitCode::rtrue(void) {
         Produce::rtrue(Emit::tree());
     }
     
    diff --git a/docs/runtime-module/2-emt.html b/docs/runtime-module/2-emt.html
    index 35033e314..0963cc0e6 100644
    --- a/docs/runtime-module/2-emt.html
    +++ b/docs/runtime-module/2-emt.html
    @@ -98,7 +98,7 @@ reference points. But as newly-created packages they are initially empty.
         Packaging::incarnate(Packaging::get_unit(main_emission_tree, I"synoptic")->the_package);
         return main_emission_tree;
     }
    -inter_tree *Emit::tree(void) {
    +inter_tree *Emit::tree(void) {
         return main_emission_tree;
     }
     
    @@ -110,7 +110,7 @@ reference points. But as newly-created packages they are initially empty.
         return InterTree::warehouse(Emit::tree());
     }
     
    -inter_bookmark *Emit::at(void) {
    +inter_bookmark *Emit::at(void) {
         return Packaging::at(Emit::tree());
     }
     
    @@ -140,7 +140,7 @@ of inter_ti var
     

    -void Emit::holster_iname(value_holster *VH, inter_name *iname) {
    +void Emit::holster_iname(value_holster *VH, inter_name *iname) {
         if (Holsters::non_void_context(VH)) {
             inter_ti v1 = 0, v2 = 0;
             Emit::to_value_pair(&v1, &v2, iname);
    diff --git a/docs/runtime-module/2-hrr.html b/docs/runtime-module/2-hrr.html
    index ffb755ba9..2fc47fe6c 100644
    --- a/docs/runtime-module/2-hrr.html
    +++ b/docs/runtime-module/2-hrr.html
    @@ -2051,7 +2051,7 @@ at which this array should be placed, by calling, e.g., 
     
     
    -inter_name *Hierarchy::find(int id) {
    +inter_name *Hierarchy::find(int id) {
         return HierarchyLocations::find(Emit::tree(), id);
     }
     
    @@ -2074,7 +2074,7 @@ package holding it. (P

    -inter_name *Hierarchy::make_iname_in(int id, package_request *P) {
    +inter_name *Hierarchy::make_iname_in(int id, package_request *P) {
         return HierarchyLocations::find_in_package(Emit::tree(), id, P, EMPTY_WORDING,
             NULL, -1, NULL);
     }
    @@ -2136,7 +2136,7 @@ available", using the following, which creates a socket. Again, see
     

    -void Hierarchy::make_available(inter_name *iname) {
    +void Hierarchy::make_available(inter_name *iname) {
         text_stream *ma_as = Produce::get_translation(iname);
         if (Str::len(ma_as) == 0) ma_as = InterNames::to_text(iname);
         PackageTypes::get(Emit::tree(), I"_linkage");
    @@ -2213,7 +2213,7 @@ will automatically trip, in order to enforce the layout rules.
     

    -package_request *Hierarchy::package_within(int hap_id, package_request *super) {
    +package_request *Hierarchy::package_within(int hap_id, package_request *super) {
         return HierarchyLocations::attach_new_package(Emit::tree(), NULL, super, hap_id);
     }
     
    diff --git a/docs/runtime-module/4-rsp.html b/docs/runtime-module/4-rsp.html index f9157f4fe..006169ce8 100644 --- a/docs/runtime-module/4-rsp.html +++ b/docs/runtime-module/4-rsp.html @@ -72,128 +72,223 @@ function togglePopup(material_id) {

    In this section we keep track of response texts.

    -

    §1. Responses are texts — which may be either literals or text substitutions — +


    + +

    §1. Introduction. Responses are texts — which may be either literals or text substitutions — occurring inside the body of rules, and marked out (A), (B), (C), ... within -that rule. This enables them to be manipulated or changed. +that rule. This enables them to be manipulated or changed. For example: +

    + +
    +Report an actor taking (this is the standard report taking rule):
    +    if the action is not silent:
    +        if the actor is the player:
    +            say "Taken." (A);
    +        otherwise:
    +            say "[The actor] [pick] up [the noun]." (B).
    +
    +

    In effect there is a two-element array attached to this rule, one holding +the current response (A), the other (B). These are identified by an index called +the "marker", which counts from 0: so (A) is 0, (B) is 1. +

    + +

    Those original appearances inside the rule are called the "cues". The texts are +stored as Text Substitutions, even if, as in example (A) here, they do not +actually involve any substituting. (It's simpler to have a common format, and in +any case these are the exception.) All of the difficulties attendant on text +substitutions apply here, too. Note, for example, that (B) refers to the "actor", +a shared variable which is not normally visible from here: +

    + +
    +To grab is a verb.
    +When play begins:
    +    now the standard report taking rule response (B) is "[The actor] [grab] [the noun]."
    +
    +

    Here, "actor" has to be read in the context of the standard report taking rule's +stack frame, not in the stack for the "when play begins" rule. +

    + +

    Each time a cue is found, a response_message object is created, as follows:

     typedef struct response_message {
    -    struct rule *responding_rule;  named rule in which this response occurs
    -    int response_marker;  0 for A, 1 for B, and so on up
    -    struct text_substitution *original_text;
    +    struct rule *the_rule;  to which this is a response
    +    int the_marker;  0 for A, 1 for B, and so on up
    +    struct text_substitution *the_ts;
         struct stack_frame *original_stack_frame;
    -    struct inter_name *resp_iname;
    +    struct inter_name *value_iname;
         struct inter_name *constant_iname;
    -    struct package_request *resp_package;
    +    struct inter_name *launcher_iname;
         int launcher_compiled;
    -    int via_I6;  if responding to a rule defined by I6 code, not source text
    -    int via_I6_routine_compiled;
    +    int via_Inter;  if responding to a rule defined by Inter code, not source text
    +    int via_Inter_routine_compiled;
         CLASS_DEFINITION
     } response_message;
     
    • The structure response_message is private to this section.
    -

    §2. Continuing with our naming convention for text resources at runtime, here -is the "launcher" routine for a response: +

    §2. Note that each response has its own package, which is stored inside the package +of the rule to which it responds. +

    + +

    It occasionally happens that assertion sentences have changed the wording of a +response long before any code is compiled, and therefore before this call, +through a sentence like:

    -inter_name *Responses::response_launcher_iname(response_message *resp) {
    -    return resp->resp_iname;
    -}
    +The print empty inventory rule response (A) is "I got nothing."
     
    -

    §3. Each response is itself a value at run-time, and the following compiles -its name in the output code: +

    This would cause RW, the replacement wording, below to be "I got nothing.".

    -inter_name *Responses::response_constant_iname(rule *R, int marker) {
    -    response_message *RM = Rules::get_response(R, marker);
    -    if (RM == NULL) return NULL;
    -    if (RM->constant_iname == NULL) internal_error("no response value");
    -    return RM->constant_iname;
    -}
    -
    -

    §4. The following is called in response to a usage of a text followed by a -response marker; for example, -

    - -
    -

    say "You can't open [the noun]." (A);

    -
    - -

    We compile it as the name of the response's "launcher" routine; that is, as -the launcher for response (A) of the rule currently being compiled. -

    - -

    The original text, "You can't open [the noun]." is then remembered as if -it were a text substitution — as of course it is, but it may be supplanted -at run-time, or even before that. (For simplicity we choose to treat the text -as a substitution even if, in fact, it's just literal text.) All of the -problems usually attendant on text substitutions apply here, too; we -need to remember the stack frame for later. -

    - -

    Thus the above source text will produce not only a TX_R_* launcher routine, -but also (in most cases) a TX_S_* text substitution routine. -

    - -
    -response_message *Responses::response_cue(value_holster *VH, rule *owner, int marker,
    -    wording W, stack_frame *frame, int via_I6) {
    +response_message *Responses::response_cue(rule *R, int marker, wording W, stack_frame *frame) {
         response_message *resp = CREATE(response_message);
    -    resp->original_stack_frame = Frames::boxed_frame(frame);
    -    resp->responding_rule = owner;
    -    resp->response_marker = marker;
    -    resp->original_text = TextSubstitutions::new_text_substitution(W, frame, owner, marker);
    +    resp->original_stack_frame = frame;
    +    resp->the_rule = R;
    +    resp->the_marker = marker;
         resp->launcher_compiled = FALSE;
    -    resp->via_I6 = via_I6;
    -    resp->via_I6_routine_compiled = FALSE;
    -    resp->resp_package = Hierarchy::package_within(RESPONSES_HAP, RTRules::package(resp->responding_rule));
    -    resp->resp_iname = Hierarchy::make_iname_in(AS_BLOCK_CONSTANT_HL, resp->resp_package);
    -    resp->constant_iname = Hierarchy::make_iname_in(AS_CONSTANT_HL, resp->resp_package);
    -    if (VH) {
    -        if (Holsters::non_void_context(VH)) {
    -            EmitCode::val_iname(K_value, Responses::response_launcher_iname(resp));
    -        }
    -    }
    +    resp->via_Inter = FALSE;
    +    resp->via_Inter_routine_compiled = FALSE;
    +
    +    package_request *PR = Hierarchy::package_within(RESPONSES_HAP, RTRules::package(R));
    +    resp->constant_iname = Hierarchy::make_iname_in(AS_CONSTANT_HL, PR);
    +    resp->value_iname = Hierarchy::make_iname_in(AS_BLOCK_CONSTANT_HL, PR);
    +    resp->launcher_iname = Hierarchy::make_iname_in(LAUNCHER_HL, PR);
    +
    +    Rules::set_response(R, marker, resp);
    +
    +    wording RW = Rules::get_response_replacement_wording(R, marker);
    +    if (Wordings::nonempty(RW)) W = RW;
    +    resp->the_ts = TextSubstitutions::new_text_substitution(W, frame, R, marker);
    +    TextSubstitutions::value_iname(resp->the_ts);
    +
         return resp;
     }
     
    -

    §5. Response launchers can be compiled in sets, but not quite all at once. -The following code is quadratic in the number of responses, but it really -doesn't matter, since so little is done and the response count can't be -enormous. +

    §3. Some access functions:

    -void Responses::compile_response_launchers(void) {
    +inter_name *Responses::response_launcher_iname(response_message *resp) {
    +    return resp->value_iname;
    +}
    +
    +inter_name *Responses::response_constant_iname(rule *R, int marker) {
    +    response_message *resp = Rules::get_response(R, marker);
    +    if (resp == NULL) return NULL;
    +    if (resp->constant_iname == NULL) internal_error("no response value");
    +    return resp->constant_iname;
    +}
    +
    +stack_frame *Responses::frame_for_response(rule *R, int marker) {
    +    response_message *resp = Rules::get_response(R, marker);
    +    if (resp == NULL) return NULL;
    +    return resp->original_stack_frame;
    +}
    +
    +

    §4. How rules gain responses. There are two ways a rule can get a new response. Firstly, and the way most +Inform authors do it: +

    + +
    +say "[The actor] [pick] up [the noun]." (B).
    +
    +

    Will cause Responses::set_via_source_text to be called. This compiles Inter +code suitable for the response to be called (i.e., printed), setting up the cue +and attaching it to its rule in the process. +

    + +

    Note the use of Local Parking (in imperative) to stash local values before the +evaluation: and see TextSubstitutions::compile_function for where those are +retrieved. +

    + +
    +void Responses::set_via_source_text(value_holster *VH, rule *R, int marker, wording SW) {
    +    stack_frame *frame = Frames::current_stack_frame();
    +    int downs = LocalParking::park(frame);
    +    response_message *resp =
    +        Responses::response_cue(R, marker, SW, Frames::boxed_frame(frame));
    +    EmitCode::val_iname(K_value, Responses::response_launcher_iname(resp));
    +    while (downs > 0) { EmitCode::up(); downs--; }
    +}
    +
    +

    §5. Secondly, a lower-level technique used by extensions to give responses even +to rules defined in Inter kits rather than by source text: +

    + +
    +The requested actions require persuasion rule translates into Inter as
    +    "REQUESTED_ACTIONS_REQUIRE_R" with
    +     "[The noun] [have] better things to do." (A).
    +
    +

    Which causes the following to be called: +

    + +
    +void Responses::set_via_translation(rule *R, int marker, wording SW) {
    +    response_message *resp = Responses::response_cue(R, marker, SW, NULL);
    +    resp->via_Inter = TRUE;
    +}
    +
    +

    §6. Compilation. Values and launchers for responses are then compiled in due course by the +following coroutine (see How To Compile (in core)): +

    + +
    +int Responses::compilation_coroutine(void) {
    +    int N = 0;
         response_message *resp;
         LOOP_OVER(resp, response_message) {
             if (resp->launcher_compiled == FALSE) {
                 resp->launcher_compiled = TRUE;
    -            Compile the actual launcher5.1;
    -            if ((resp->via_I6) && (resp->via_I6_routine_compiled == FALSE))
    -                If the response is via I6, compile the necessary routine for this rule5.2;
    +            N++;
    +            Compile resources needed by this response6.1;
    +        }
    +        if ((resp->via_Inter) && (resp->via_Inter_routine_compiled == FALSE)) {
    +            response_message *r2;
    +            LOOP_OVER(r2, response_message)
    +                if (r2->the_rule == resp->the_rule)
    +                    r2->via_Inter_routine_compiled = TRUE;
    +            N++;
    +            Compile the response-handler function for this rule6.2;
             }
         }
    +    return N;
     }
     
    -

    §5.1. Each response is itself a value, and the launcher routine consists only of -a call to an activity based on that value: -

    - -

    Compile the actual launcher5.1 = +

    §6.1. Each response compiles to a text value like so:

    -    package_request *R = resp->resp_package;
    -    inter_name *launcher = Hierarchy::make_iname_in(LAUNCHER_HL, R);
    +                            small block:
    +    value ----------------> CONSTANT_PACKED_TEXT_STORAGE
    +                            launcher function ----------------------> ...
    +
    +

    Thus, printing this value at runtime calls the launcher function. This in +turn runs the "issuing the response text" activity, though it does it via +a function defined in BasicInformKit. +

    - packaging_state save = Functions::begin(launcher); +

    Compile resources needed by this response6.1 = +

    + +
    +    text_substitution *ts = resp->the_ts;
    +    inter_name *ts_value_iname = TextSubstitutions::value_iname(ts);
    +    inter_name *rc_iname =
    +        Responses::response_constant_iname(resp->the_rule, resp->the_marker);
    +    Emit::response(rc_iname, resp->the_rule, resp->the_marker, ts_value_iname);
    +
    +    TextLiterals::compile_value_to(resp->value_iname, resp->launcher_iname);
    +
    +    packaging_state save = Functions::begin(resp->launcher_iname);
     
         inter_name *iname = Responses::response_constant_iname(
    -        resp->responding_rule, resp->response_marker);
    +        resp->the_rule, resp->the_marker);
     
         inter_name *rname = Hierarchy::find(RESPONSEVIAACTIVITY_HL);
         EmitCode::call(rname);
    @@ -202,24 +297,17 @@ a call to an activity based on that value:
         EmitCode::up();
     
         Functions::end(save);
    -
    -    save = EmitArrays::begin(resp->resp_iname, K_value);
    -    EmitArrays::iname_entry(Hierarchy::find(CONSTANT_PACKED_TEXT_STORAGE_HL));
    -    EmitArrays::iname_entry(launcher);
    -    EmitArrays::end(save);
     
    -
    • This code is used in §5.
    -

    §5.2. Something skated over above is that responses can also be created when the -source text defines a rule only as an I6 routine. For example: +

    • This code is used in §6.
    +

    §6.2. Something skated over above is that responses can also be created when the +source text defines a rule only as an Inter routine. For example:

    -
    -

    The hack mode rule translates into I6 as "HACK_MODE_ON_R" with "Hack mode on." (A).

    -
    - -

    Responses like this one are "via I6", and they cause us to create a support -routine for the rule, called in this case HACK_MODE_ON_RM. The rule then -calls +

    +The hack mode rule translates into Inter as "HACK_MODE_ON_R" with "Hack mode on." (A).
    +
    +

    Responses like this one are "via Inter", and they cause us to create a handler +function for the rule, called (say) HACK_MODE_ON_RM. The rule then calls:

    @@ -231,15 +319,16 @@ calls
     
         HACK_MODE_ON_RM('a');
     
    -

    to return the current text of (A) without printing it. Speed is not of the -essence here. +

    to return the current text of (A) without printing it. Speed is not of the essence; +and note that the response-handler is created in the package for the rule to which +it responds.

    -

    If the response is via I6, compile the necessary routine for this rule5.2 = +

    Compile the response-handler function for this rule6.2 =

    -    inter_name *responder_iname = RTRules::get_handler_definition(resp->responding_rule);
    +    inter_name *responder_iname = RTRules::get_handler_definition(resp->the_rule);
         packaging_state save = Functions::begin(responder_iname);
         inter_symbol *code_s = LocalVariables::new_other_as_symbol(I"code");
         inter_symbol *val_s = LocalVariables::new_other_as_symbol(I"val");
    @@ -322,20 +411,19 @@ essence here.
             EmitCode::down();
                 response_message *r2;
                 LOOP_OVER(r2, response_message) {
    -                if (r2->responding_rule == resp->responding_rule) {
    +                if (r2->the_rule == resp->the_rule) {
                         EmitCode::inv(CASE_BIP);
                         EmitCode::down();
    -                        EmitCode::val_number((inter_ti) ('A' + r2->response_marker));
    +                        EmitCode::val_number((inter_ti) ('A' + r2->the_marker));
                             EmitCode::code();
                             EmitCode::down();
                                 EmitCode::inv(STORE_BIP);
                                 EmitCode::down();
                                     EmitCode::ref_symbol(K_value, str_s);
    -                                EmitCode::val_iname(K_value, r2->resp_iname);
    +                                EmitCode::val_iname(K_value, r2->value_iname);
                                 EmitCode::up();
                             EmitCode::up();
                         EmitCode::up();
    -                    r2->via_I6_routine_compiled = TRUE;
                     }
                 }
             EmitCode::up();
    @@ -384,55 +472,22 @@ essence here.
     
         Functions::end(save);
     
    -
    • This code is used in §5.
    -

    §6. So much for the launchers. We also have to compile the response values, -and some run-time tables which will enable the I6 template code to keep -track of the content of each response. +

    • This code is used in §6.
    +

    §7. There's then one function and one array left to compile:

    -void Responses::compile_responses(void) {
    -    Compile the array holding the current text of each response6.1;
    -    Compile the PrintResponse routine6.2;
    -    Compile the Response Divisions array6.3;
    -    TextSubstitutions::compile_text_routines_in_response_mode();
    +void Responses::compile_synoptic_resources(void) {
    +    Compile the PrintResponse routine7.1;
    +    Compile the Response Divisions array7.2;
     }
     
    -

    §6.1. Note that each rule is allowed to tell us that it already has a better -text for the response than the one we first created. -

    - -

    Compile the array holding the current text of each response6.1 = -

    - -
    -    rule *R;
    -    LOOP_OVER(R, rule) {
    -        int marker;
    -        for (marker = 0; marker < 26; marker++) {
    -            response_message *resp = Rules::get_response(R, marker);
    -            if (resp) {
    -                text_substitution *ts = resp->original_text;
    -                wording W = Rules::get_response_content(R, marker);
    -                if (Wordings::nonempty(W)) {  i.e., if the rule gives us a better text
    -                    current_sentence = Rules::get_response_sentence(R, marker);
    -                    ts = TextSubstitutions::new_text_substitution(W, NULL, R, marker);
    -                    resp->original_text->tr_done_already = TRUE;
    -                }
    -                inter_name *ts_value_iname = TextSubstitutions::value_iname(ts);
    -                inter_name *rc_iname = Responses::response_constant_iname(R, marker);
    -                Emit::response(rc_iname, R, marker, ts_value_iname);
    -            }
    -        }
    -    }
    -
    -
    • This code is used in §6.
    -

    §6.2. This is in effect a big switch statement, so it's not fast; but as usual +

    §7.1. This is in effect a big switch statement, so it's not fast; but as usual with printing routines it really doesn't need to be. Given a response value, say R_14_RESP_B, we print its current text, say response (B) for R_14.

    -

    Compile the PrintResponse routine6.2 = +

    Compile the PrintResponse routine7.1 =

    @@ -441,8 +496,8 @@ say R_14_RESP_B
         inter_symbol *R_s = LocalVariables::new_other_as_symbol(I"R");
         response_message *resp;
         LOOP_OVER(resp, response_message) {
    -        inter_name *iname = Responses::response_constant_iname(resp->responding_rule,
    -            resp->response_marker);
    +        inter_name *iname = Responses::response_constant_iname(resp->the_rule,
    +            resp->the_marker);
             EmitCode::inv(IF_BIP);
             EmitCode::down();
                 EmitCode::inv(EQ_BIP);
    @@ -454,7 +509,7 @@ say R_14_RESP_B
                 EmitCode::down();
                     EmitCode::call(Hierarchy::find(RULEPRINTINGRULE_HL));
                     EmitCode::down();
    -                    EmitCode::val_iname(K_value, RTRules::iname(resp->responding_rule));
    +                    EmitCode::val_iname(K_value, RTRules::iname(resp->the_rule));
                     EmitCode::up();
                     EmitCode::inv(PRINT_BIP);
                     EmitCode::down();
    @@ -462,7 +517,7 @@ say R_14_RESP_B
                     EmitCode::up();
                     EmitCode::inv(PRINTCHAR_BIP);
                     EmitCode::down();
    -                    EmitCode::val_number((inter_ti) ('A' + resp->response_marker));
    +                    EmitCode::val_number((inter_ti) ('A' + resp->the_marker));
                     EmitCode::up();
                     EmitCode::inv(PRINT_BIP);
                     EmitCode::down();
    @@ -473,30 +528,30 @@ say R_14_RESP_B
         }
         Functions::end(save);
     
    -
    • This code is used in §6.
    -

    §6.3. The following array is used only by the testing command RESPONSES, and -enables the I6 template to print out all known responses at run-time, +

    • This code is used in §7.
    +

    §7.2. The following array is used only by the testing command RESPONSES, and +enables the Inter template to print out all known responses at run-time, divided up by the extensions containing the rules which produce them.

    -

    Compile the Response Divisions array6.3 = +

    Compile the Response Divisions array7.2 =

         inter_name *iname = Hierarchy::find(RESPONSEDIVISIONS_HL);
         packaging_state save = EmitArrays::begin(iname, K_value);
         inform_extension *group_E = NULL;
    -    Make a ResponseDivisions entry6.3.1;
    +    Make a ResponseDivisions entry7.2.1;
         LOOP_OVER(group_E, inform_extension)
    -        Make a ResponseDivisions entry6.3.1;
    +        Make a ResponseDivisions entry7.2.1;
         EmitArrays::numeric_entry(0);
         EmitArrays::numeric_entry(0);
         EmitArrays::numeric_entry(0);
         EmitArrays::end(save);
         Hierarchy::make_available(iname);
     
    -
    • This code is used in §6.
    -

    §6.3.1. Make a ResponseDivisions entry6.3.1 = +

    • This code is used in §7.
    +

    §7.2.1. Make a ResponseDivisions entry7.2.1 =

    @@ -508,13 +563,13 @@ divided up by the extensions containing the rules which produce them.
                     tally++;
                     inform_extension *E = Extensions::corresponding_to(
                         Lexer::file_of_origin(Wordings::first_wn(R->name)));
    -                if (E == group_E) Start a possible run of matches6.3.1.1
    -                else End a possible run of matches6.3.1.2;
    +                if (E == group_E) Start a possible run of matches7.2.1.1
    +                else End a possible run of matches7.2.1.2;
                 }
    -    End a possible run of matches6.3.1.2;
    +    End a possible run of matches7.2.1.2;
     
    -
    • This code is used in §6.3 (twice).
    -

    §6.3.1.1. Start a possible run of matches6.3.1.1 = +

    • This code is used in §7.2 (twice).
    +

    §7.2.1.1. Start a possible run of matches7.2.1.1 =

    @@ -530,8 +585,8 @@ divided up by the extensions containing the rules which produce them.
             EmitArrays::numeric_entry((inter_ti) (tally));
         }
     
    - -

    §6.3.1.2. End a possible run of matches6.3.1.2 = +

    +

    §7.2.1.2. End a possible run of matches7.2.1.2 =

    @@ -540,170 +595,7 @@ divided up by the extensions containing the rules which produce them.
             contiguous_match = FALSE;
         }
     
    -
    • This code is used in §6.3.1 (twice).
    -

    §7.

    - -
    -stack_frame *Responses::frame_for_response(response_message *resp) {
    -    if (resp == NULL) return NULL;
    -    return resp->original_stack_frame;
    -}
    -
    -

    §8. As mentioned above, assertions in the source text can change the text of -a given response even at compile time. But the rules code looks after that: -

    - -
    -void Responses::assert_response_value(rule *R, int marker, wording W) {
    -    Rules::now_rule_needs_response(R, marker, W);
    -}
    -
    -

    §9. When we index a response, we also provide a paste button for the source -text to assert a change: -

    - -
    -void Responses::index_response(OUTPUT_STREAM, rule *R, int marker, response_message *resp) {
    -    WRITE("&nbsp;&nbsp;&nbsp;&nbsp;");
    -    HTML_OPEN_WITH("span",
    -        "style=\"color: #ffffff; "
    -        "font-family: 'Courier New', Courier, monospace; background-color: #8080ff;\"");
    -    WRITE("&nbsp;&nbsp;%c&nbsp;&nbsp; ", 'A' + marker);
    -    HTML_CLOSE("span");
    -    HTML_OPEN_WITH("span", "style=\"color: #000066;\"");
    -    WRITE("%+W", resp->original_text->unsubstituted_text);
    -    HTML_CLOSE("span");
    -    WRITE("&nbsp;&nbsp;");
    -    TEMPORARY_TEXT(S)
    -    WRITE_TO(S, "%+W response (%c)", R->name, 'A' + marker);
    -    PasteButtons::paste_text(OUT, S);
    -    WRITE("&nbsp;<i>name</i>");
    -    WRITE("&nbsp;");
    -    Str::clear(S);
    -    WRITE_TO(S, "The %+W response (%c) is \"New text.\".");
    -    PasteButtons::paste_text(OUT, S);
    -    WRITE("&nbsp;<i>set</i>");
    -    DISCARD_TEXT(S)
    -}
    -
    -

    §10.

    - -
    -int Responses::get_marker_from_response_spec(parse_node *rs) {
    -    if (Rvalues::is_CONSTANT_of_kind(rs, K_response)) {
    -        wording SW = Node::get_text(rs);
    -        if ((Wordings::length(SW) >= 2) && (<response-letter>(Wordings::one_word(Wordings::last_wn(SW)-1))))
    -            return <<r>>;
    -    }
    -    return -1;
    -}
    -
    -

    §11. To complete the code on strings, we just need the top-level routine which -handles the compilation of a general string literal. There are actually three -ways we might not even be compiling an I7 text value here: -

    - -
    • (a) If the specification is flagged "explicit", we're using this as a device -to hold low-level I6 property values such as parse_name routines, and we -simply compile the text raw. -
    • (b) If we're in quotation mode, that means the text is destined to be in an -I6 "box" statement, which needs it to be formed in an eccentric way. -
    • (c) If we're in bibliographic mode, we're compiling not to the I6 program -but to something like an XML description of its metadata, where again the -text needs to be printed in a particular way. -
    -
    -void Responses::compile_general(value_holster *VH, parse_node *str) {
    -    wording SW = Node::get_text(str);
    -    if (Annotations::read_int(str, explicit_literal_ANNOT)) {
    -        if (Node::get_explicit_iname(str)) {
    -            if (Holsters::non_void_context(VH)) {
    -                Emit::holster_iname(VH, Node::get_explicit_iname(str));
    -            } else internal_error("unvalued SCG");
    -        } else {
    -            int A = Annotations::read_int(str, constant_number_ANNOT);
    -            if (Holsters::non_void_context(VH))
    -                Holsters::holster_pair(VH, LITERAL_IVAL, (inter_ti) A);
    -        }
    -    } else {
    -        if (Wordings::empty(SW)) internal_error("Text no longer available for CONSTANT/TEXT");
    -        This is going to make a valid I7 text value11.1;
    -    }
    -}
    -
    -

    §11.1. Responses take the form -

    - -
    -    "blah blah blah" ( letter )
    -
    -

    so the penultimate word, if it's there, is the letter. -

    - -

    This is going to make a valid I7 text value11.1 = -

    - -
    -    if ((Wordings::length(SW) >= 2) && (<response-letter>(Wordings::one_word(Wordings::last_wn(SW)-1))))
    -        This is a response11.1.1
    -    else This isn't a response11.1.2;
    -
    -
    • This code is used in §11.
    -

    §11.1.1. This is a response11.1.1 = -

    - -
    -    int code = <<r>>;
    -    if ((rule_being_compiled == NULL) ||
    -        (Rules::rule_allows_responses(rule_being_compiled) == FALSE)) {
    -        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_ResponseContextWrong),
    -            "lettered responses can only be used in named rules",
    -            "not in any of the other contexts in which quoted text can appear.");
    -        return;
    -    }
    -    if (Rules::get_response(rule_being_compiled, code)) {
    -        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_ResponseDuplicated),
    -            "this duplicates a response letter",
    -            "which is not allowed: if a bracketed letter like (A) is used to mark "
    -            "some text as a response, then it can only occur once in its rule.");
    -        return;
    -    }
    -    stack_frame *frame = Frames::current_stack_frame();
    -    if (Holsters::non_void_context(VH)) {
    -        int downs = LocalParking::park(frame);
    -        response_message *resp =
    -            Responses::response_cue(VH, rule_being_compiled, code, SW,
    -                Frames::boxed_frame(frame), FALSE);
    -        Rules::set_response(rule_being_compiled, code, resp);
    -        while (downs > 0) { EmitCode::up(); downs--; }
    -    }
    -
    - -

    §11.1.2. This isn't a response11.1.2 = -

    - -
    -    if (Annotations::read_int(str, text_unescaped_ANNOT)) {
    -        if (CompileValues::compiling_in_constant_mode()) {
    -            inter_name *val_iname = TextLiterals::to_value_unescaped(SW);
    -            Emit::holster_iname(VH, val_iname);
    -        } else {
    -            inter_name *val_iname = TextLiterals::to_value_unescaped(SW);
    -            Emit::holster_iname(VH, val_iname);
    -        }
    -    } else if (Vocabulary::test_flags(Wordings::first_wn(SW), TEXTWITHSUBS_MC)) {
    -        TextSubstitutions::text_substitution_cue(VH, SW);
    -    } else {
    -        if (CompileValues::compiling_in_constant_mode()) {
    -            inter_name *val_iname = TextLiterals::to_value(SW);
    -            Emit::holster_iname(VH, val_iname);
    -        } else {
    -            inter_name *val_iname = TextLiterals::to_value(SW);
    -            Emit::holster_iname(VH, val_iname);
    -        }
    -    }
    -
    - +
    • This code is used in §7.2.1 (twice).
    diff --git a/docs/runtime-module/4-tl.html b/docs/runtime-module/4-tl.html index 208e39d0f..d21b2ec4f 100644 --- a/docs/runtime-module/4-tl.html +++ b/docs/runtime-module/4-tl.html @@ -114,18 +114,22 @@ it will always be a function.
     inter_name *TextLiterals::small_block(inter_name *content) {
         inter_name *small_block = Enclosures::new_small_block_for_constant();
    -    packaging_state save = EmitArrays::begin(small_block, K_value);
    +    TextLiterals::compile_value_to(small_block, content);
    +    return small_block;
    +}
    +
    +void TextLiterals::compile_value_to(inter_name *at, inter_name *content) {
    +    packaging_state save = EmitArrays::begin(at, K_value);
         EmitArrays::iname_entry(Hierarchy::find(CONSTANT_PACKED_TEXT_STORAGE_HL));
         EmitArrays::iname_entry(content);
         EmitArrays::end(save);
    -    return small_block;
     }
     

    §2. Default value. The default text is empty:

    -inter_name *TextLiterals::default_text(void) {
    +inter_name *TextLiterals::default_text(void) {
         return TextLiterals::small_block(Hierarchy::find(EMPTY_TEXT_PACKED_HL));
     }
     
    @@ -139,7 +143,7 @@ source text, and nothing else. void TextLiterals::suppress_quote_expansion(wording W) { wn_quote_suppressed = Wordings::first_wn(W); } -int TextLiterals::suppressing_on(wording W) { +int TextLiterals::suppressing_on(wording W) { if ((wn_quote_suppressed >= 0) && (Wordings::first_wn(W) == wn_quote_suppressed)) return TRUE; return FALSE; @@ -152,11 +156,11 @@ much later in the process. See -inter_name *TextLiterals::to_value(wording W) { +inter_name *TextLiterals::to_value(wording W) { return TextLiterals::to_value_inner(W, FALSE); } -inter_name *TextLiterals::to_value_unescaped(wording W) { +inter_name *TextLiterals::to_value_unescaped(wording W) { return TextLiterals::to_value_inner(W, TRUE); } diff --git a/docs/runtime-module/4-ts.html b/docs/runtime-module/4-ts.html index 3ae02bc50..494596cbb 100644 --- a/docs/runtime-module/4-ts.html +++ b/docs/runtime-module/4-ts.html @@ -72,7 +72,7 @@ function togglePopup(material_id) {

    In this section we compile text with substitutions.

    -
    +

    §1. Runtime representation. Text substitutions arise from source text such as:

    @@ -161,7 +161,7 @@ wording "the [fox speed] brow

    -void TextSubstitutions::text_substitution_cue(value_holster *VH, wording W) {
    +void TextSubstitutions::text_substitution_cue(value_holster *VH, wording W) {
         text_substitution *ts = NULL;
         switch (VH->vhmode_wanted) {
             case INTER_VAL_VHMODE: Cue in value context2.1; break;
    @@ -229,7 +229,6 @@ case of a response to a rule, since those are never perishable.
         struct stack_frame *using_frame;  for cases where possible
     
         struct inter_name *ts_value_iname;  the I6 array for this
    -    int ts_value_iname_used;
         struct inter_name *ts_function_iname;  the routine to implement it
     
         struct rule *responding_to_rule;
    @@ -238,7 +237,7 @@ case of a response to a rule, since those are never perishable.
         CLASS_DEFINITION
     } text_substitution;
     
    -
    • The structure text_substitution is accessed in 4/rsp and here.
    +
    • The structure text_substitution is private to this section.

    §4. Two inames are involved here:

    @@ -247,14 +246,8 @@ case of a response to a rule, since those are never perishable. value ----------------> CONSTANT_PACKED_TEXT_STORAGE or CONSTANT_PERISHABLE_TEXT_STORAGE function ----------------------> ...
    -

    While the function is always compiled, the small block is only compiled if -the value iname is ever actually requested. (Sometimes when making alternative -responses we just want to make the function.) -

    -
    -inter_name *TextSubstitutions::value_iname(text_substitution *ts) {
    -    ts->ts_value_iname_used = TRUE;
    +inter_name *TextSubstitutions::value_iname(text_substitution *ts) {
         return ts->ts_value_iname;
     }
     
    @@ -262,10 +255,12 @@ responses we just want to make the function.)
         return ts->ts_function_iname;
     }
     
    -

    §5.

    +

    §5. Note that this function is called both when cues are detected (above), and +also when responses are created — see Responses. +

    -text_substitution *TextSubstitutions::new_text_substitution(wording W,
    +text_substitution *TextSubstitutions::new_text_substitution(wording W,
         stack_frame *frame, rule *R, int marker) {
     
         text_substitution *ts = CREATE(text_substitution);
    @@ -273,9 +268,8 @@ responses we just want to make the function.)
             internal_error("Too late for further text substitutions");
         ts->unsubstituted_text = Wordings::first_word(W);
         ts->sentence_using_this = current_sentence;
    -    if (R) {
    -        ts->using_frame = NULL;
    -    } else {
    +    ts->using_frame = NULL;
    +    if (R == NULL) {
             stack_frame new_frame = Frames::new();
             ts->using_frame = Frames::boxed_frame(&new_frame);
             if (frame) LocalVariableSlates::append(ts->using_frame, frame);
    @@ -293,7 +287,6 @@ responses we just want to make the function.)
         if (R) P = RTRules::package(R);
         package_request *PR = Hierarchy::package_within(LITERALS_HAP, P);
         ts->ts_value_iname = Hierarchy::make_iname_in(TEXT_SUBSTITUTION_HL, PR);
    -    ts->ts_value_iname_used = FALSE;
         ts->ts_function_iname = Hierarchy::make_iname_in(TEXT_SUBSTITUTION_FN_HL, PR);
     
         ts->owning_point = current_sentence;
    @@ -304,17 +297,14 @@ responses we just want to make the function.)
         return ts;
     }
     
    -

    §6. Compilation.

    +

    §6. Compilation. Functions for substitutions are then compiled in due course by the following coroutine +(see How To Compile (in core)): +

    -int TextSubstitutions::compilation_coroutine(void) {
    -    return TextSubstitutions::compile_as_needed(FALSE);
    -}
    -
     text_substitution *latest_ts_compiled = NULL;
     int compiling_text_routines_mode = FALSE;  used for better problem messages
    -int TextSubstitutions::compile_as_needed(int in_response_mode) {
    -    Responses::compile_response_launchers();
    +int TextSubstitutions::compilation_coroutine(void) {
         int N = 0;
         compiling_text_routines_mode = TRUE;
         while (TRUE) {
    @@ -323,13 +313,11 @@ responses we just want to make the function.)
             else ts = NEXT_OBJECT(latest_ts_compiled, text_substitution);
             if (ts == NULL) break;
             latest_ts_compiled = ts;
    -        int responding = FALSE;
    -        if (ts->responding_to_rule) responding = TRUE;
    -        if ((responding == in_response_mode) && (ts->tr_done_already == FALSE)) {
    +        if (ts->tr_done_already == FALSE) {
                 ts->tr_done_already = TRUE;
                 int makes_local_refs = TextSubstitutions::compile_function(ts);
    -            if (ts->ts_value_iname_used)
    -                TextSubstitutions::compile_value(ts->ts_value_iname, ts->ts_function_iname, makes_local_refs);
    +            TextSubstitutions::compile_value(ts->ts_value_iname,
    +                ts->ts_function_iname, makes_local_refs);
             }
             N++;
         }
    @@ -350,41 +338,41 @@ compiling it.
     
     
     text_substitution *current_ts_being_compiled = NULL;
    -int TextSubstitutions::compile_function(text_substitution *ts) {
    +int TextSubstitutions::compile_function(text_substitution *ts) {
         LOGIF(TEXT_SUBSTITUTIONS, "Compiling text routine %d %08x %W\n",
             ts->allocation_id, (int) (ts->using_frame), ts->unsubstituted_text);
     
         current_ts_being_compiled = ts;
         packaging_state save = Functions::begin(ts->ts_function_iname);
    -    stack_frame *frame = ts->using_frame;
    -    if ((ts->responding_to_rule) && (ts->responding_to_marker >= 0)) {
    -        response_message *resp = Rules::get_response(
    -            ts->responding_to_rule, ts->responding_to_marker);
    -        if (resp) frame = Responses::frame_for_response(resp);
    -    }
    -    if (frame) LocalVariableSlates::append(Frames::current_stack_frame(), frame);
    +    stack_frame *frame = NULL;
    +    Give the function access to shared variables visible to its user7.1;
    +
         LocalVariables::monitor_local_parsing(Frames::current_stack_frame());
    -
    -    Compile a say-phrase7.1;
    -
    +    Compile some debugging text7.2;
    +    Compile a say-phrase7.3;
         int makes_local_references =
             LocalVariables::local_parsed_recently(Frames::current_stack_frame());
    -    if (makes_local_references) {
    -        Produce::push_code_position(Emit::tree(), Produce::begin_position(Emit::tree()), Inter::Bookmarks::snapshot(Emit::at()));
    -        LocalParking::retrieve(frame);
    -        Produce::pop_code_position(Emit::tree());
    -    }
    +    if (makes_local_references) Insert code at start of function to retrieve parked values7.4;
    +
         Functions::end(save);
         current_ts_being_compiled = NULL;
         return makes_local_references;
     }
     
    -

    §7.1. Of course, if we used Inform's standard phrase mechanism exactly, then -the whole thing would be circular, because that would once again generate -a request for a new text substitution to be compiled later... +

    §7.1. Give the function access to shared variables visible to its user7.1 =

    -

    Compile a say-phrase7.1 = +

    +    frame = Responses::frame_for_response(ts->responding_to_rule, ts->responding_to_marker);
    +    if (frame == NULL) frame = ts->using_frame;
    +    if (frame) LocalVariableSlates::append(Frames::current_stack_frame(), frame);
    +
    +
    • This code is used in §7.
    +

    §7.2. In DEBUG mode, there's an option to print the unsubstituted text instead — +note the rtrue here, which stops the function from proceeding. +

    + +

    Compile some debugging text7.2 =

    @@ -395,7 +383,8 @@ a request for a new text substitution to be compiled later...
                 EmitCode::down();
                     EmitCode::inv(IF_BIP);
                     EmitCode::down();
    -                    EmitCode::val_iname(K_number, Hierarchy::find(SUPPRESS_TEXT_SUBSTITUTION_HL));
    +                    EmitCode::val_iname(K_number,
    +                        Hierarchy::find(SUPPRESS_TEXT_SUBSTITUTION_HL));
                         EmitCode::code();
                         EmitCode::down();
                             EmitCode::inv(PRINT_BIP);
    @@ -411,7 +400,17 @@ a request for a new text substitution to be compiled later...
                 EmitCode::up();
             EmitCode::up();
         }
    +
    +
    • This code is used in §7.
    +

    §7.3. Of course, if we used Inform's standard phrase mechanism exactly, then +the whole thing would be circular, because that would once again generate +a request for a new text substitution to be compiled later... +

    +

    Compile a say-phrase7.3 = +

    + +
         parse_node *ts_code_block = Node::new(IMPERATIVE_NT);
         CompilationUnits::assign_to_same_unit(ts_code_block, ts->owning_point);
         ts_code_block->next = Node::new(UNKNOWN_NT);
    @@ -424,24 +423,28 @@ a request for a new text substitution to be compiled later...
         EmitCode::rtrue();
     
    • This code is used in §7.
    -

    §8. See the "Responses" section for why, but we sometimes want to force -the coroutine to go through the whole queue once, then go back to the -start again — which would be very inefficient except that in this mode -we aren't doing very much; most TSs will be passed quickly over. +

    §7.4. Where a text substitution refers to local variables in the caller, +Local Parking (in imperative) is used to pass it the current values of those +locals; and this means that the function must begin by retrieving those values. +But since we have already compiled most of the function, we have to go back to +the start temporarily to insert this extra code. +

    + +

    Insert code at start of function to retrieve parked values7.4 =

    -void TextSubstitutions::compile_text_routines_in_response_mode(void) {
    -    latest_ts_compiled = NULL;
    -    TextSubstitutions::compile_as_needed(TRUE);
    -    latest_ts_compiled = NULL;
    -}
    +    Produce::push_code_position(Emit::tree(),
    +        Produce::begin_position(Emit::tree()), Inter::Bookmarks::snapshot(Emit::at()));
    +    LocalParking::retrieve(frame);
    +    Produce::pop_code_position(Emit::tree());
     
    -

    §9. It may be worth adding. Finally, the following clarifies problem messages arising from the issue of +

    • This code is used in §7.
    +

    §8. It may be worth adding. Finally, the following clarifies problem messages arising from the issue of local names being used in substitutions, since this often confuses newcomers:

    -
    define ENDING_MESSAGE_PROBLEMS_CALLBACK TextSubstitutions::append_text_substitution_proviso
    +
    define ENDING_MESSAGE_PROBLEMS_CALLBACK TextSubstitutions::append_text_substitution_proviso
     
     int it_is_not_worth_adding = FALSE;  To suppress the "It may be worth adding..."
    @@ -456,10 +459,10 @@ local names being used in substitutions, since this often confuses newcomers:
         it_is_not_worth_adding = TRUE;
     }
     
    -

    §10.

    +

    §9.

    -void TextSubstitutions::append_text_substitution_proviso(void) {
    +void TextSubstitutions::append_text_substitution_proviso(void) {
         if (it_is_not_worth_adding) return;
         if (compiling_text_routines_mode == FALSE) return;
         if ((current_ts_being_compiled) &&
    diff --git a/docs/runtime-module/5-rls.html b/docs/runtime-module/5-rls.html
    index d859128b8..e59a9b7dd 100644
    --- a/docs/runtime-module/5-rls.html
    +++ b/docs/runtime-module/5-rls.html
    @@ -122,7 +122,7 @@ function togglePopup(material_id) {
             Hierarchy::make_iname_in(RULE_FN_HL, P));
     }
     
    -package_request *RTRules::package(rule *R) {
    +package_request *RTRules::package(rule *R) {
         return R->compilation_data.rule_package;
     }
     
    @@ -132,7 +132,7 @@ function togglePopup(material_id) {
         return R->compilation_data.shell_routine_iname;
     }
     
    -inter_name *RTRules::iname(rule *R) {
    +inter_name *RTRules::iname(rule *R) {
         if (R->defn_as_I7_source) return CompileImperativeDefn::iname(R->defn_as_I7_source->body_of_defn);
         else if (R->compilation_data.rule_extern_iname) {
             if (LinkedLists::len(R->applicability_constraints) > 0) {
    @@ -153,7 +153,7 @@ function togglePopup(material_id) {
         R->compilation_data.xiname = xiname;
     }
     
    -inter_name *RTRules::get_handler_definition(rule *R) {
    +inter_name *RTRules::get_handler_definition(rule *R) {
         if (R->compilation_data.rule_extern_response_handler_iname == NULL) {
             R->compilation_data.rule_extern_response_handler_iname =
                 Hierarchy::derive_iname_in(RESPONDER_FN_HL, R->compilation_data.xiname, R->compilation_data.rule_package);
    diff --git a/docs/values-module/2-rvl.html b/docs/values-module/2-rvl.html
    index 33e5228c1..0aa79564c 100644
    --- a/docs/values-module/2-rvl.html
    +++ b/docs/values-module/2-rvl.html
    @@ -150,6 +150,15 @@ pointers:
             CONV_TO(property) }
     rule *Rvalues::to_rule(parse_node *spec) {
             CONV_TO(rule) }
    +int Rvalues::to_response_marker(parse_node *spec) {
    +    if (Rvalues::is_CONSTANT_of_kind(spec, K_response)) {
    +        wording SW = Node::get_text(spec);
    +        if ((Wordings::length(SW) >= 2) &&
    +            (<response-letter>(Wordings::one_word(Wordings::last_wn(SW)-1))))
    +            return <<r>>;
    +    }
    +    return -1;
    +}
     rulebook *Rvalues::to_rulebook(parse_node *spec) {
             CONV_TO(rulebook) }
     table *Rvalues::to_table(parse_node *spec) {
    @@ -446,7 +455,7 @@ in the proposition.
         return FALSE;
     }
     
    -int Rvalues::is_CONSTANT_of_kind(parse_node *spec, kind *K) {
    +int Rvalues::is_CONSTANT_of_kind(parse_node *spec, kind *K) {
         if ((Node::is(spec, CONSTANT_NT)) &&
             (Kinds::eq(Node::get_kind_of_value(spec), K)))
             return TRUE;
    diff --git a/inform7/Figures/memory-diagnostics.txt b/inform7/Figures/memory-diagnostics.txt
    index 4f550201e..f1e90b97e 100644
    --- a/inform7/Figures/memory-diagnostics.txt
    +++ b/inform7/Figures/memory-diagnostics.txt
    @@ -1,12 +1,12 @@
    -Total memory consumption was 278685K = 272 MB
    +Total memory consumption was 279485K = 273 MB
     
    -63.7% was used for 1358697 objects, in 311577 frames in 222 x 800K = 177600K = 173 MB:
    +63.8% was used for 1358051 objects, in 311030 frames in 223 x 800K = 178400K = 174 MB:
     
          9.3%  inter_tree_node_array                    37 x 8192 = 303104 objects, 26674336 bytes
    -     5.6%  linked_list                              28812 objects, 16134720 bytes
    +     5.5%  linked_list                              28539 objects, 15981840 bytes
          5.4%  text_stream_array                        2745 x 100 = 274500 objects, 15459840 bytes
          3.6%  parse_node                               129367 objects, 10349360 bytes
    -     2.6%  verb_conjugation                         160 objects, 7425280 bytes
    +     2.5%  verb_conjugation                         160 objects, 7425280 bytes
          2.2%  inter_symbol_array                       71 x 1024 = 72704 objects, 6400224 bytes
          1.9%  parse_node_annotation_array              345 x 500 = 172500 objects, 5531040 bytes
          1.1%  pcalc_prop_array                         24 x 1000 = 24000 objects, 3264768 bytes
    @@ -32,12 +32,12 @@ Total memory consumption was 278685K = 272 MB
          0.2%  dict_entry_array                         243 x 100 = 24300 objects, 785376 bytes
          0.2%  inter_name_generator_array               18 x 1000 = 18000 objects, 720576 bytes
          0.2%  unary_predicate_array                    16 x 1000 = 16000 objects, 640512 bytes
    -     0.1%  local_variable_array                     45 x 100 = 4500 objects, 433440 bytes
    +     0.1%  local_variable_array                     44 x 100 = 4400 objects, 423808 bytes
          0.1%  verb_usage                               1128 objects, 388032 bytes
          0.1%  scan_directory                           94 objects, 388032 bytes
          0.1%  rule                                     469 objects, 363944 bytes
          0.1%  verb_form                                386 objects, 342768 bytes
    -     0.1%  noun                                     2379 objects, 285480 bytes
    +     ----  noun                                     2379 objects, 285480 bytes
          ----  inference_subject                        665 objects, 260680 bytes
          ----  inter_annotation_array                   1 x 8192 objects, 196640 bytes
          ----  linguistic_stock_item                    3315 objects, 159120 bytes
    @@ -63,19 +63,18 @@ Total memory consumption was 278685K = 272 MB
          ----  spatial_data                             670 objects, 64320 bytes
          ----  kind_macro_definition                    9 objects, 62280 bytes
          ----  booking                                  860 objects, 61920 bytes
    -     ----  scenes_rcd_data                          1880 objects, 60160 bytes
          ----  actions_rcd_data                         1880 objects, 60160 bytes
    +     ----  scenes_rcd_data                          1880 objects, 60160 bytes
          ----  command_grammar                          130 objects, 58240 bytes
          ----  pcalc_func_array                         1 x 1000 objects, 56032 bytes
          ----  kind_constructor                         77 objects, 55440 bytes
    -     ----  stack_frame_box                          577 objects, 55392 bytes
          ----  cg_token                                 603 objects, 53064 bytes
          ----  property_inference_data                  1315 objects, 52600 bytes
          ----  ap_clause_array                          2 x 400 = 800 objects, 51264 bytes
          ----  cg_line                                  230 objects, 47840 bytes
          ----  table                                    7 objects, 45528 bytes
    -     ----  text_substitution                        436 objects, 45344 bytes
          ----  inter_node_list                          750 objects, 42000 bytes
    +     ----  text_substitution                        436 objects, 41856 bytes
          ----  activity_list_array                      1 x 1000 objects, 40032 bytes
          ----  anl_clause_array                         1 x 1000 objects, 40032 bytes
          ----  to_family_data                           496 objects, 39680 bytes
    @@ -85,6 +84,7 @@ Total memory consumption was 278685K = 272 MB
          ----  regions_data                             670 objects, 32160 bytes
          ----  HTML_tag_array                           1 x 1000 objects, 32032 bytes
          ----  property_permission                      96 objects, 30720 bytes
    +     ----  stack_frame_box                          304 objects, 29184 bytes
          ----  verb_sense                               403 objects, 29016 bytes
          ----  shared_variable_set_array                6 x 100 = 600 objects, 28992 bytes
          ----  heading                                  198 objects, 28512 bytes
    @@ -101,9 +101,9 @@ Total memory consumption was 278685K = 272 MB
          ----  parse_node_tree                          20 objects, 17280 bytes
          ----  method                                   341 objects, 16368 bytes
          ----  understanding_reference_array            2 x 100 = 200 objects, 16064 bytes
    -     ----  linked_list_item_array                   1 x 1000 objects, 16032 bytes
          ----  action_name_list_array                   1 x 1000 objects, 16032 bytes
          ----  match_avinue_array                       1 x 1000 objects, 16032 bytes
    +     ----  linked_list_item_array                   1 x 1000 objects, 16032 bytes
          ----  to_phrase_request                        59 objects, 15576 bytes
          ----  adjective                                137 objects, 14248 bytes
          ----  booking_list                             407 objects, 13024 bytes
    @@ -135,8 +135,8 @@ Total memory consumption was 278685K = 272 MB
          ----  method_set                               104 objects, 3328 bytes
          ----  kind_constructor_comparison_schema_array 1 x 100 objects, 3232 bytes
          ----  instance_usage_array                     1 x 200 objects, 3232 bytes
    -     ----  definition                               44 objects, 3168 bytes
          ----  compatibility_specification              66 objects, 3168 bytes
    +     ----  definition                               44 objects, 3168 bytes
          ----  inform_extension                         19 objects, 3040 bytes
          ----  property_of_value_storage                93 objects, 2976 bytes
          ----  either_or_property_data                  62 objects, 2976 bytes
    @@ -150,8 +150,8 @@ Total memory consumption was 278685K = 272 MB
          ----  semver_range                             22 objects, 2288 bytes
          ----  scene                                    1 object, 2096 bytes
          ----  use_option                               29 objects, 1856 bytes
    -     ----  pronoun_usage                            42 objects, 1680 bytes
          ----  pipeline_step                            14 objects, 1680 bytes
    +     ----  pronoun_usage                            42 objects, 1680 bytes
          ----  activity_crossref_array                  1 x 100 objects, 1632 bytes
          ----  table_contribution_array                 1 x 100 objects, 1632 bytes
          ----  plugin                                   25 objects, 1600 bytes
    @@ -159,8 +159,8 @@ Total memory consumption was 278685K = 272 MB
          ----  noun_filter_token                        22 objects, 1408 bytes
          ----  inter_annotation_form                    35 objects, 1400 bytes
          ----  special_meaning_holder                   33 objects, 1320 bytes
    -     ----  constant_phrase                          20 objects, 1280 bytes
          ----  build_script                             40 objects, 1280 bytes
    +     ----  constant_phrase                          20 objects, 1280 bytes
          ----  invocation_options_array                 1 x 100 objects, 1224 bytes
          ----  direction_inference_data                 30 objects, 1200 bytes
          ----  hierarchy_metadatum                      15 objects, 1200 bytes
    @@ -168,8 +168,8 @@ Total memory consumption was 278685K = 272 MB
          ----  pipeline_stage                           19 objects, 912 bytes
          ----  table_column                             16 objects, 896 bytes
          ----  inbuild_requirement                      22 objects, 880 bytes
    -     ----  code_generation                          1 object, 864 bytes
          ----  control_structure_phrase                 12 objects, 864 bytes
    +     ----  code_generation                          1 object, 864 bytes
          ----  cached_understanding                     21 objects, 840 bytes
          ----  runtime_kind_structure                   13 objects, 832 bytes
          ----  phrase_option_array                      1 x 100 objects, 824 bytes
    @@ -178,18 +178,18 @@ Total memory consumption was 278685K = 272 MB
          ----  inter_data_type                          14 objects, 784 bytes
          ----  submodule_identity                       23 objects, 736 bytes
          ----  inform_language                          6 objects, 672 bytes
    -     ----  inter_warehouse_room                     10 objects, 640 bytes
    -     ----  relation_guard                           5 objects, 640 bytes
          ----  I6T_intervention                         8 objects, 640 bytes
    -     ----  named_rulebook_outcome                   15 objects, 600 bytes
    +     ----  relation_guard                           5 objects, 640 bytes
    +     ----  inter_warehouse_room                     10 objects, 640 bytes
          ----  inbuild_search_result                    15 objects, 600 bytes
    +     ----  named_rulebook_outcome                   15 objects, 600 bytes
          ----  rulebook_outcome                         17 objects, 544 bytes
          ----  small_word_set                           11 objects, 528 bytes
          ----  inform_kit                               5 objects, 520 bytes
          ----  implication                              13 objects, 520 bytes
          ----  inference_family                         11 objects, 440 bytes
    -     ----  equation                                 4 objects, 416 bytes
          ----  i6_memory_setting                        13 objects, 416 bytes
    +     ----  equation                                 4 objects, 416 bytes
          ----  dval_written                             10 objects, 400 bytes
          ----  article_usage                            8 objects, 384 bytes
          ----  bp_family                                12 objects, 384 bytes
    @@ -236,10 +236,10 @@ Total memory consumption was 278685K = 272 MB
          ----  kind_template_definition                 1 object, 40 bytes
          ----  by_routine_bp_data                       1 object, 40 bytes
     
    -36.2% was used for memory not allocated for objects:
    +36.1% was used for memory not allocated for objects:
     
    -    15.8%  text stream storage                      45362796 bytes in 282770 claims
    -     3.5%  dictionary storage                       10015744 bytes in 17805 claims
    +    15.8%  text stream storage                      45362852 bytes in 282770 claims
    +     3.4%  dictionary storage                       10015744 bytes in 17805 claims
          ----  sorting                                  968 bytes in 3 claims
          2.5%  source text                              7200000 bytes in 3 claims
          3.7%  source text details                      10800000 bytes in 2 claims
    @@ -254,5 +254,5 @@ Total memory consumption was 278685K = 272 MB
          ----  emitter array storage                    175296 bytes in 2064 claims
          ----  code generation workspace for objects    19736 bytes in 13 claims
     
    -20.1% was overhead - 57492712 bytes = 56145K = 54 MB
    +20.4% was overhead - 58504120 bytes = 57132K = 55 MB
     
    diff --git a/inform7/Figures/timings-diagnostics.txt b/inform7/Figures/timings-diagnostics.txt
    index b0c7276ef..0828409b7 100644
    --- a/inform7/Figures/timings-diagnostics.txt
    +++ b/inform7/Figures/timings-diagnostics.txt
    @@ -1,30 +1,31 @@
     100.0% in inform7 run
    -     64.0% in compilation to Inter
    -         24.5% in //ImperativeDefinitions::compile_first_block//
    -          8.4% in //Sequence::compile_function_resources//
    -          7.0% in //Responses::compile_responses//
    -          5.7% in //InferenceSubjects::emit_all//
    +     64.6% in compilation to Inter
    +         24.8% in //ImperativeDefinitions::compile_first_block//
    +         11.7% in //Sequence::compile_function_resources//
    +          5.9% in //InferenceSubjects::emit_all//
               3.6% in //MajorNodes::pre_pass//
    -          3.1% in //MajorNodes::pass_1//
    +          3.5% in //Responses::compile_synoptic_resources//
    +          3.3% in //MajorNodes::pass_1//
               1.9% in //RTRules::RulePrintingRule_routine//
    +          1.7% in //ImperativeDefinitions::assess_all//
               1.7% in //RTRules::rulebooks_array_array//
    -          1.5% in //ImperativeDefinitions::assess_all//
               1.0% in //RTVerbs::ConjugateVerb//
               0.5% in //RTRules::compile_rulebooks//
    -          0.5% in //World::stage_V//
               0.3% in //MajorNodes::pass_2//
               0.3% in //RTRelations::compile_defined_relations//
    +          0.3% in //World::stage_V//
               0.1% in //RTCommandGrammars::compile_all//
               0.1% in //RTKinds::compile_data_type_support_routines//
               0.1% in //Task::make_built_in_kind_constructors//
    -          2.9% not specifically accounted for
    -     33.4% in running Inter pipeline
    -         12.0% in step preparation
    +          0.1% in //World::stages_II_and_III//
    +          2.8% not specifically accounted for
    +     33.0% in running Inter pipeline
    +         11.7% in step preparation
               9.4% in inter step 2/14: link
    -          7.1% in inter step 14/14: generate inform6 -> auto.inf
    +          7.0% in inter step 14/14: generate inform6 -> auto.inf
               0.7% in inter step 10/14: consolidate-text
    +          0.5% in inter step 9/14: make-identifiers-unique
               0.3% in inter step 13/14: eliminate-redundant-operations
    -          0.3% in inter step 9/14: make-identifiers-unique
               0.1% in inter step 11/14: reconcile-verbs
               0.1% in inter step 12/14: eliminate-redundant-labels
               0.1% in inter step 4/14: parse-linked-matter
    @@ -34,4 +35,4 @@
               0.1% in inter step 8/14: inspect-plugs
               2.1% not specifically accounted for
           1.9% in supervisor
    -      0.6% not specifically accounted for
    +      0.4% not specifically accounted for
    diff --git a/inform7/assertions-module/Chapter 3/Translation Requests.w b/inform7/assertions-module/Chapter 3/Translation Requests.w
    index 0f7a00eeb..4ec250bc0 100644
    --- a/inform7/assertions-module/Chapter 3/Translation Requests.w	
    +++ b/inform7/assertions-module/Chapter 3/Translation Requests.w	
    @@ -332,10 +332,8 @@ void Translations::plus_responses(parse_node *p, rule *R) {
     		Translations::plus_responses(p->down->next, R);
     	} else {
     		if ((Node::get_text(p))) {
    -			int code = <>;
    -			response_message *resp = Responses::response_cue(NULL, R,
    -				code, Node::get_text(p), NULL, TRUE);
    -			Rules::set_response(R, code, resp);
    +			int marker = <>;
    +			Responses::set_via_translation(R, marker, Node::get_text(p));
     		} else {
     			StandardProblems::sentence_problem(Task::syntax_tree(),
     				_p_(PM_I6ResponsesAwry),
    diff --git a/inform7/assertions-module/Chapter 4/Assertions.w b/inform7/assertions-module/Chapter 4/Assertions.w
    index b1810ffea..e5564caab 100644
    --- a/inform7/assertions-module/Chapter 4/Assertions.w	
    +++ b/inform7/assertions-module/Chapter 4/Assertions.w	
    @@ -1761,7 +1761,7 @@ but that's not evident without a lot of contextual checking.
     		(Rvalues::is_CONSTANT_of_kind(val, K_text))) {
     		rule *R = Rvalues::to_rule(constant);
     		int c = Annotations::read_int(constant, response_code_ANNOT);
    -		Responses::assert_response_value(R, c, Node::get_text(val));
    +		Rules::now_rule_needs_response(R, c, Node::get_text(val));
     		return;
     	}
     
    diff --git a/inform7/assertions-module/Chapter 6/Rules.w b/inform7/assertions-module/Chapter 6/Rules.w
    index 8bf15a06a..ad601e134 100644
    --- a/inform7/assertions-module/Chapter 6/Rules.w	
    +++ b/inform7/assertions-module/Chapter 6/Rules.w	
    @@ -516,7 +516,7 @@ rule_response Rules::new_rule_response(void) {
     	return rr;
     }
     
    -wording Rules::get_response_content(rule *R, int code) {
    +wording Rules::get_response_replacement_wording(rule *R, int code) {
     	if (R == NULL) return EMPTY_WORDING;
     	if ((code < 0) || (code >= 26)) return EMPTY_WORDING;
     	return R->responses[code].content;
    diff --git a/inform7/core-module/Chapter 1/How To Compile.w b/inform7/core-module/Chapter 1/How To Compile.w
    index 451b1a4e2..286b34d7e 100644
    --- a/inform7/core-module/Chapter 1/How To Compile.w	
    +++ b/inform7/core-module/Chapter 1/How To Compile.w	
    @@ -218,7 +218,7 @@ so on. Those absolute basics are made here.
     	BENCH(PhraseRequests::invoke_to_begin)
     	BENCH(Closures::compile_closures)
     	BENCH(Sequence::compile_function_resources)
    -	BENCH(Responses::compile_responses)
    +	BENCH(Responses::compile_synoptic_resources)
     	BENCH(Sequence::compile_literal_resources)
     	BENCH(RTRelations::compile_defined_relations)
     	BENCH(Sequence::compile_function_resources)
    @@ -306,6 +306,7 @@ void Sequence::compile_function_resources(void) {
     		if (PhraseRequests::compilation_coroutine() > 0)       repeat = TRUE;
     		if (ListTogether::compilation_coroutine() > 0)         repeat = TRUE;
     		if (LoopingOverScope::compilation_coroutine() > 0)     repeat = TRUE;
    +		if (Responses::compilation_coroutine() > 0)            repeat = TRUE;
     		if (TextSubstitutions::compilation_coroutine() > 0)    repeat = TRUE;
     		if (DeferredPropositions::compilation_coroutine() > 0) repeat = TRUE;
     
    diff --git a/inform7/imperative-module/Chapter 2/Compile Rvalues.w b/inform7/imperative-module/Chapter 2/Compile Rvalues.w
    index 52a69e64c..ab1459959 100644
    --- a/inform7/imperative-module/Chapter 2/Compile Rvalues.w	
    +++ b/inform7/imperative-module/Chapter 2/Compile Rvalues.w	
    @@ -163,7 +163,7 @@ kinds of value:
     		return;
     	}
     	if (Kinds::eq(kind_of_constant, K_text)) {
    -		Responses::compile_general(VH, value);
    +		CompileRvalues::text(VH, value);
     		return;
     	}
     	#ifdef IF_MODULE
    @@ -243,3 +243,78 @@ contexts by using a tilde: |~attr|.
     			Emit::holster_iname(VH, RTProperties::iname(prn));
     		}
     	}
    +
    +@ Texts can be compiled in four different ways, so the following splits into
    +four cases. Note that responses take the form
    += (text)
    +	"blah blah blah" ( letter )
    +=
    +so the penultimate word, if it's there, is the letter.
    +
    +=
    +void CompileRvalues::text(value_holster *VH, parse_node *str) {
    +	if (Holsters::non_void_context(VH) == FALSE) internal_error("text in void context");
    +	if (Annotations::read_int(str, explicit_literal_ANNOT)) {
    +		@;
    +	} else {
    +		wording SW = Node::get_text(str);
    +		int unescaped = Annotations::read_int(str, text_unescaped_ANNOT);
    +		if (Wordings::empty(SW)) internal_error("text without wording");
    +		if ((Wordings::length(SW) >= 2) &&
    +			((Wordings::one_word(Wordings::last_wn(SW)-1)))) {
    +			@;
    +		} else if ((unescaped == 0) &&
    +				(Vocabulary::test_flags(Wordings::first_wn(SW), TEXTWITHSUBS_MC))) {
    +			@;
    +		} else if (unescaped) {				
    +			@;
    +		} else {
    +			@;
    +		}
    +	}
    +}
    +
    +@ Not explicit in the sense of an advisory sticker on an Eminem CD: explicit
    +in providing a text stream for its content, rather than a wording from the
    +source text. (This usually means it has been manufactured somewhere in the
    +compiler, rather than parsed from the source.)
    +
    +@ =
    +	if (Node::get_explicit_iname(str)) {
    +		if (Holsters::non_void_context(VH)) {
    +			Emit::holster_iname(VH, Node::get_explicit_iname(str));
    +		} else internal_error("unvalued SCG");
    +	} else {
    +		int A = Annotations::read_int(str, constant_number_ANNOT);
    +		if (Holsters::non_void_context(VH))
    +			Holsters::holster_pair(VH, LITERAL_IVAL, (inter_ti) A);
    +	}
    +
    +@ =
    +	int marker = <>;
    +	if ((rule_being_compiled == NULL) ||
    +		(Rules::rule_allows_responses(rule_being_compiled) == FALSE)) {
    +		StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_ResponseContextWrong),
    +			"lettered responses can only be used in named rules",
    +			"not in any of the other contexts in which quoted text can appear.");
    +		return;
    +	}
    +	if (Rules::get_response(rule_being_compiled, marker)) {
    +		StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_ResponseDuplicated),
    +			"this duplicates a response letter",
    +			"which is not allowed: if a bracketed letter like (A) is used to mark "
    +			"some text as a response, then it can only occur once in its rule.");
    +		return;
    +	}
    +	Responses::set_via_source_text(VH, rule_being_compiled, marker, SW);
    +
    +@ =
    +	TextSubstitutions::text_substitution_cue(VH, SW);
    +
    +@ =
    +	inter_name *val_iname = TextLiterals::to_value_unescaped(SW);
    +	Emit::holster_iname(VH, val_iname);
    +
    +@ =
    +	inter_name *val_iname = TextLiterals::to_value(SW);
    +	Emit::holster_iname(VH, val_iname);
    diff --git a/inform7/imperative-module/Chapter 4/Compile Schemas.w b/inform7/imperative-module/Chapter 4/Compile Schemas.w
    index 245978a7d..614baf6e8 100644
    --- a/inform7/imperative-module/Chapter 4/Compile Schemas.w	
    +++ b/inform7/imperative-module/Chapter 4/Compile Schemas.w	
    @@ -115,7 +115,7 @@ Here |this| is the term in question, and |other| the other of the two.
     	if ((m & ADOPT_LOCAL_STACK_FRAME_ISSBM) &&
     		(Rvalues::is_CONSTANT_of_kind(other->constant, K_response))) {
     		rule_to_which_this_is_a_response = Rvalues::to_rule(other->constant);
    -		response_marker_within_that_rule = Responses::get_marker_from_response_spec(other->constant);
    +		response_marker_within_that_rule = Rvalues::to_response_marker(other->constant);
     	}
     	kind *K = NULL;
     	if (m & CAST_TO_KIND_OF_OTHER_TERM_ISSBM) K = other->term_checked_as_kind;
    diff --git a/inform7/index-module/Chapter 2/Rules.w b/inform7/index-module/Chapter 2/Rules.w
    index 94a251d2d..688e28cf8 100644
    --- a/inform7/index-module/Chapter 2/Rules.w	
    +++ b/inform7/index-module/Chapter 2/Rules.w	
    @@ -87,7 +87,7 @@ int IXRules::index(OUTPUT_STREAM, rule *R, rulebook *owner, rule_context rc) {
     		if (R->responses[l].message) {
     			if (c == 0) Index::extra_div_open_nested(OUT, 1000000+R->allocation_id, 2);
     			else HTML_TAG("br");
    -			Responses::index_response(OUT, R, l, R->responses[l].message);
    +			IXRules::index_response(OUT, R, l, R->responses[l].message);
     			c++;
     		}
     	if (c > 0) Index::extra_div_close_nested(OUT);
    @@ -737,3 +737,31 @@ int IXRules::phrase_fits_rule_context(id_body *idb, rule_context rc) {
     	if (Scenes::rcd_scene(&(idb->runtime_context_data)) != rc.scene_context) return FALSE;
     	return TRUE;
     }
    +
    +@ When we index a response, we also provide a paste button for the source
    +text to assert a change:
    +
    +=
    +void IXRules::index_response(OUTPUT_STREAM, rule *R, int marker, response_message *resp) {
    +	WRITE("    ");
    +	HTML_OPEN_WITH("span",
    +		"style=\"color: #ffffff; "
    +		"font-family: 'Courier New', Courier, monospace; background-color: #8080ff;\"");
    +	WRITE("  %c   ", 'A' + marker);
    +	HTML_CLOSE("span");
    +	HTML_OPEN_WITH("span", "style=\"color: #000066;\"");
    +	WRITE("%+W", resp->the_ts->unsubstituted_text);
    +	HTML_CLOSE("span");
    +	WRITE("  ");
    +	TEMPORARY_TEXT(S)
    +	WRITE_TO(S, "%+W response (%c)", R->name, 'A' + marker);
    +	PasteButtons::paste_text(OUT, S);
    +	WRITE(" name");
    +	WRITE(" ");
    +	Str::clear(S);
    +	WRITE_TO(S, "The %+W response (%c) is \"New text.\".");
    +	PasteButtons::paste_text(OUT, S);
    +	WRITE(" set");
    +	DISCARD_TEXT(S)
    +}
    +
    diff --git a/inform7/runtime-module/Chapter 4/Responses.w b/inform7/runtime-module/Chapter 4/Responses.w
    index f4706bdf7..5ccc1c3ea 100644
    --- a/inform7/runtime-module/Chapter 4/Responses.w	
    +++ b/inform7/runtime-module/Chapter 4/Responses.w	
    @@ -2,113 +2,197 @@
     
     In this section we keep track of response texts.
     
    -@ Responses are texts -- which may be either literals or text substitutions --
    +@h Introduction.
    +Responses are texts -- which may be either literals or text substitutions --
     occurring inside the body of rules, and marked out (A), (B), (C), ... within
    -that rule. This enables them to be manipulated or changed.
    +that rule. This enables them to be manipulated or changed. For example:
    += (text as Inform 7)
    +Report an actor taking (this is the standard report taking rule):
    +	if the action is not silent:
    +		if the actor is the player:
    +			say "Taken." (A);
    +		otherwise:
    +			say "[The actor] [pick] up [the noun]." (B).
    +=
    +In effect there is a two-element array attached to this rule, one holding
    +the current response (A), the other (B). These are identified by an index called
    +the "marker", which counts from 0: so (A) is 0, (B) is 1.
    +
    +Those original appearances inside the rule are called the "cues". The texts are
    +stored as //Text Substitutions//, even if, as in example (A) here, they do not
    +actually involve any substituting. (It's simpler to have a common format, and in
    +any case these are the exception.) All of the difficulties attendant on text
    +substitutions apply here, too. Note, for example, that (B) refers to the "actor",
    +a shared variable which is not normally visible from here:
    += (text as Inform 7)
    +To grab is a verb.
    +When play begins:
    +	now the standard report taking rule response (B) is "[The actor] [grab] [the noun]."
    +=
    +Here, "actor" has to be read in the context of the standard report taking rule's
    +stack frame, not in the stack for the "when play begins" rule.
    +
    +Each time a cue is found, a |response_message| object is created, as follows:
     
     =
     typedef struct response_message {
    -	struct rule *responding_rule; /* named rule in which this response occurs */
    -	int response_marker; /* 0 for A, 1 for B, and so on up */
    -	struct text_substitution *original_text;
    +	struct rule *the_rule; /* to which this is a response */
    +	int the_marker; /* 0 for A, 1 for B, and so on up */
    +	struct text_substitution *the_ts;
     	struct stack_frame *original_stack_frame;
    -	struct inter_name *resp_iname;
    +	struct inter_name *value_iname;
     	struct inter_name *constant_iname;
    -	struct package_request *resp_package;
    +	struct inter_name *launcher_iname;
     	int launcher_compiled;
    -	int via_I6; /* if responding to a rule defined by I6 code, not source text */
    -	int via_I6_routine_compiled;
    +	int via_Inter; /* if responding to a rule defined by Inter code, not source text */
    +	int via_Inter_routine_compiled;
     	CLASS_DEFINITION
     } response_message;
     
    -@ Continuing with our naming convention for text resources at runtime, here
    -is the "launcher" routine for a response:
    +@ Note that each response has its own package, which is stored inside the package
    +of the rule to which it responds.
    +
    +It occasionally happens that assertion sentences have changed the wording of a
    +response long before any code is compiled, and therefore before this call,
    +through a sentence like:
    += (text as Inform 7)
    +The print empty inventory rule response (A) is "I got nothing."
    +=
    +This would cause |RW|, the replacement wording, below to be |"I got nothing."|.
     
     =
    -inter_name *Responses::response_launcher_iname(response_message *resp) {
    -	return resp->resp_iname;
    -}
    -
    -@ Each response is itself a value at run-time, and the following compiles
    -its name in the output code:
    -
    -=
    -inter_name *Responses::response_constant_iname(rule *R, int marker) {
    -	response_message *RM = Rules::get_response(R, marker);
    -	if (RM == NULL) return NULL;
    -	if (RM->constant_iname == NULL) internal_error("no response value");
    -	return RM->constant_iname;
    -}
    -
    -@ The following is called in response to a usage of a text followed by a
    -response marker; for example,
    -
    ->> say "You can't open [the noun]." (A);
    -
    -We compile it as the name of the response's "launcher" routine; that is, as
    -the launcher for response (A) of the rule currently being compiled.
    -
    -The original text, |"You can't open [the noun]."| is then remembered as if
    -it were a text substitution -- as of course it is, but it may be supplanted
    -at run-time, or even before that. (For simplicity we choose to treat the text
    -as a substitution even if, in fact, it's just literal text.) All of the
    -problems usually attendant on text substitutions apply here, too; we
    -need to remember the stack frame for later.
    -
    -Thus the above source text will produce not only a |TX_R_*| launcher routine,
    -but also (in most cases) a |TX_S_*| text substitution routine.
    -
    -=
    -response_message *Responses::response_cue(value_holster *VH, rule *owner, int marker,
    -	wording W, stack_frame *frame, int via_I6) {
    +response_message *Responses::response_cue(rule *R, int marker, wording W, stack_frame *frame) {
     	response_message *resp = CREATE(response_message);
    -	resp->original_stack_frame = Frames::boxed_frame(frame);
    -	resp->responding_rule = owner;
    -	resp->response_marker = marker;
    -	resp->original_text = TextSubstitutions::new_text_substitution(W, frame, owner, marker);
    +	resp->original_stack_frame = frame;
    +	resp->the_rule = R;
    +	resp->the_marker = marker;
     	resp->launcher_compiled = FALSE;
    -	resp->via_I6 = via_I6;
    -	resp->via_I6_routine_compiled = FALSE;
    -	resp->resp_package = Hierarchy::package_within(RESPONSES_HAP, RTRules::package(resp->responding_rule));
    -	resp->resp_iname = Hierarchy::make_iname_in(AS_BLOCK_CONSTANT_HL, resp->resp_package);
    -	resp->constant_iname = Hierarchy::make_iname_in(AS_CONSTANT_HL, resp->resp_package);
    -	if (VH) {
    -		if (Holsters::non_void_context(VH)) {
    -			EmitCode::val_iname(K_value, Responses::response_launcher_iname(resp));
    -		}
    -	}
    +	resp->via_Inter = FALSE;
    +	resp->via_Inter_routine_compiled = FALSE;
    +
    +	package_request *PR = Hierarchy::package_within(RESPONSES_HAP, RTRules::package(R));
    +	resp->constant_iname = Hierarchy::make_iname_in(AS_CONSTANT_HL, PR);
    +	resp->value_iname = Hierarchy::make_iname_in(AS_BLOCK_CONSTANT_HL, PR);
    +	resp->launcher_iname = Hierarchy::make_iname_in(LAUNCHER_HL, PR);
    +
    +	Rules::set_response(R, marker, resp);
    +
    +	wording RW = Rules::get_response_replacement_wording(R, marker);
    +	if (Wordings::nonempty(RW)) W = RW;
    +	resp->the_ts = TextSubstitutions::new_text_substitution(W, frame, R, marker);
    +	TextSubstitutions::value_iname(resp->the_ts);
    +
     	return resp;
     }
     
    -@ Response launchers can be compiled in sets, but not quite all at once.
    -The following code is quadratic in the number of responses, but it really
    -doesn't matter, since so little is done and the response count can't be
    -enormous.
    +@ Some access functions:
     
     =
    -void Responses::compile_response_launchers(void) {
    +inter_name *Responses::response_launcher_iname(response_message *resp) {
    +	return resp->value_iname;
    +}
    +
    +inter_name *Responses::response_constant_iname(rule *R, int marker) {
    +	response_message *resp = Rules::get_response(R, marker);
    +	if (resp == NULL) return NULL;
    +	if (resp->constant_iname == NULL) internal_error("no response value");
    +	return resp->constant_iname;
    +}
    +
    +stack_frame *Responses::frame_for_response(rule *R, int marker) {
    +	response_message *resp = Rules::get_response(R, marker);
    +	if (resp == NULL) return NULL;
    +	return resp->original_stack_frame;
    +}
    +
    +@h How rules gain responses.
    +There are two ways a rule can get a new response. Firstly, and the way most
    +Inform authors do it:
    += (text as Inform 7)
    +say "[The actor] [pick] up [the noun]." (B).
    +=
    +Will cause //Responses::set_via_source_text// to be called. This compiles Inter
    +code suitable for the response to be called (i.e., printed), setting up the cue
    +and attaching it to its rule in the process.
    +
    +Note the use of //imperative: Local Parking// to stash local values before the
    +evaluation: and see //TextSubstitutions::compile_function// for where those are
    +retrieved.
    +
    +=
    +void Responses::set_via_source_text(value_holster *VH, rule *R, int marker, wording SW) {
    +	stack_frame *frame = Frames::current_stack_frame();
    +	int downs = LocalParking::park(frame);
    +	response_message *resp =
    +		Responses::response_cue(R, marker, SW, Frames::boxed_frame(frame));
    +	EmitCode::val_iname(K_value, Responses::response_launcher_iname(resp));
    +	while (downs > 0) { EmitCode::up(); downs--; }
    +}
    +
    +@ Secondly, a lower-level technique used by extensions to give responses even
    +to rules defined in Inter kits rather than by source text:
    += (text as Inform 7)
    +The requested actions require persuasion rule translates into Inter as
    +	"REQUESTED_ACTIONS_REQUIRE_R" with
    +	 "[The noun] [have] better things to do." (A).
    +=
    +Which causes the following to be called:
    +
    +=
    +void Responses::set_via_translation(rule *R, int marker, wording SW) {
    +	response_message *resp = Responses::response_cue(R, marker, SW, NULL);
    +	resp->via_Inter = TRUE;
    +}
    +
    +@h Compilation.
    +Values and launchers for responses are then compiled in due course by the
    +following coroutine (see //core: How To Compile//):
    +
    +=
    +int Responses::compilation_coroutine(void) {
    +	int N = 0;
     	response_message *resp;
     	LOOP_OVER(resp, response_message) {
     		if (resp->launcher_compiled == FALSE) {
     			resp->launcher_compiled = TRUE;
    -			@;
    -			if ((resp->via_I6) && (resp->via_I6_routine_compiled == FALSE))
    -				@;
    +			N++;
    +			@;
    +		}
    +		if ((resp->via_Inter) && (resp->via_Inter_routine_compiled == FALSE)) {
    +			response_message *r2;
    +			LOOP_OVER(r2, response_message)
    +				if (r2->the_rule == resp->the_rule)
    +					r2->via_Inter_routine_compiled = TRUE;
    +			N++;
    +			@;
     		}
     	}
    +	return N;
     }
     
    -@ Each response is itself a value, and the launcher routine consists only of
    -a call to an activity based on that value:
    +@ Each response compiles to a text value like so:
    += (text)
    +	                        small block:
    +	value ----------------> CONSTANT_PACKED_TEXT_STORAGE
    +	                        launcher function ----------------------> ...
    +=
    +Thus, printing this value at runtime calls the launcher function. This in
    +turn runs the "issuing the response text" activity, though it does it via
    +a function defined in //BasicInformKit//.
     
    -@ =
    -	package_request *R = resp->resp_package;
    -	inter_name *launcher = Hierarchy::make_iname_in(LAUNCHER_HL, R);
    +@ =
    +	text_substitution *ts = resp->the_ts;
    +	inter_name *ts_value_iname = TextSubstitutions::value_iname(ts);
    +	inter_name *rc_iname =
    +		Responses::response_constant_iname(resp->the_rule, resp->the_marker);
    +	Emit::response(rc_iname, resp->the_rule, resp->the_marker, ts_value_iname);
     
    -	packaging_state save = Functions::begin(launcher);
    +	TextLiterals::compile_value_to(resp->value_iname, resp->launcher_iname);
    +
    +	packaging_state save = Functions::begin(resp->launcher_iname);
     
     	inter_name *iname = Responses::response_constant_iname(
    -		resp->responding_rule, resp->response_marker);
    +		resp->the_rule, resp->the_marker);
     
     	inter_name *rname = Hierarchy::find(RESPONSEVIAACTIVITY_HL);
     	EmitCode::call(rname);
    @@ -118,19 +202,13 @@ a call to an activity based on that value:
     
     	Functions::end(save);
     
    -	save = EmitArrays::begin(resp->resp_iname, K_value);
    -	EmitArrays::iname_entry(Hierarchy::find(CONSTANT_PACKED_TEXT_STORAGE_HL));
    -	EmitArrays::iname_entry(launcher);
    -	EmitArrays::end(save);
    -
     @ Something skated over above is that responses can also be created when the
    -source text defines a rule only as an I6 routine. For example:
    -
    ->> The hack mode rule translates into I6 as "HACK_MODE_ON_R" with "Hack mode on." (A).
    -
    -Responses like this one are "via I6", and they cause us to create a support
    -routine for the rule, called in this case |HACK_MODE_ON_RM|. The rule then
    -calls
    +source text defines a rule only as an Inter routine. For example:
    += (text as Inform 7)
    +The hack mode rule translates into Inter as "HACK_MODE_ON_R" with "Hack mode on." (A).
    +=
    +Responses like this one are "via Inter", and they cause us to create a handler
    +function for the rule, called (say) |HACK_MODE_ON_RM|. The rule then calls:
     = (text as Inform 6)
     	HACK_MODE_ON_RM('A');
     =
    @@ -138,11 +216,12 @@ to produce response (A), or alternatively
     = (text as Inform 6)
     	HACK_MODE_ON_RM('a');
     =
    -to return the current text of (A) without printing it. Speed is not of the
    -essence here.
    +to return the current text of (A) without printing it. Speed is not of the essence;
    +and note that the response-handler is created in the package for the rule to which
    +it responds.
     
    -@ =
    -	inter_name *responder_iname = RTRules::get_handler_definition(resp->responding_rule);
    +@ =
    +	inter_name *responder_iname = RTRules::get_handler_definition(resp->the_rule);
     	packaging_state save = Functions::begin(responder_iname);
     	inter_symbol *code_s = LocalVariables::new_other_as_symbol(I"code");
     	inter_symbol *val_s = LocalVariables::new_other_as_symbol(I"val");
    @@ -225,20 +304,19 @@ essence here.
     		EmitCode::down();
     			response_message *r2;
     			LOOP_OVER(r2, response_message) {
    -				if (r2->responding_rule == resp->responding_rule) {
    +				if (r2->the_rule == resp->the_rule) {
     					EmitCode::inv(CASE_BIP);
     					EmitCode::down();
    -						EmitCode::val_number((inter_ti) ('A' + r2->response_marker));
    +						EmitCode::val_number((inter_ti) ('A' + r2->the_marker));
     						EmitCode::code();
     						EmitCode::down();
     							EmitCode::inv(STORE_BIP);
     							EmitCode::down();
     								EmitCode::ref_symbol(K_value, str_s);
    -								EmitCode::val_iname(K_value, r2->resp_iname);
    +								EmitCode::val_iname(K_value, r2->value_iname);
     							EmitCode::up();
     						EmitCode::up();
     					EmitCode::up();
    -					r2->via_I6_routine_compiled = TRUE;
     				}
     			}
     		EmitCode::up();
    @@ -287,42 +365,14 @@ essence here.
     
     	Functions::end(save);
     
    -@ So much for the launchers. We also have to compile the response values,
    -and some run-time tables which will enable the I6 template code to keep
    -track of the content of each response.
    +@ There's then one function and one array left to compile:
     
     =
    -void Responses::compile_responses(void) {
    -	@;
    +void Responses::compile_synoptic_resources(void) {
     	@;
     	@;
    -	TextSubstitutions::compile_text_routines_in_response_mode();
     }
     
    -@ Note that each rule is allowed to tell us that it already has a better
    -text for the response than the one we first created.
    -
    -@ =
    -	rule *R;
    -	LOOP_OVER(R, rule) {
    -		int marker;
    -		for (marker = 0; marker < 26; marker++) {
    -			response_message *resp = Rules::get_response(R, marker);
    -			if (resp) {
    -				text_substitution *ts = resp->original_text;
    -				wording W = Rules::get_response_content(R, marker);
    -				if (Wordings::nonempty(W)) { /* i.e., if the rule gives us a better text */
    -					current_sentence = Rules::get_response_sentence(R, marker);
    -					ts = TextSubstitutions::new_text_substitution(W, NULL, R, marker);
    -					resp->original_text->tr_done_already = TRUE;
    -				}
    -				inter_name *ts_value_iname = TextSubstitutions::value_iname(ts);
    -				inter_name *rc_iname = Responses::response_constant_iname(R, marker);
    -				Emit::response(rc_iname, R, marker, ts_value_iname);
    -			}
    -		}
    -	}
    -
     @ This is in effect a big switch statement, so it's not fast; but as usual
     with printing routines it really doesn't need to be. Given a response value,
     say |R_14_RESP_B|, we print its current text, say response (B) for |R_14|.
    @@ -333,8 +383,8 @@ say |R_14_RESP_B|, we print its current text, say response (B) for |R_14|.
     	inter_symbol *R_s = LocalVariables::new_other_as_symbol(I"R");
     	response_message *resp;
     	LOOP_OVER(resp, response_message) {
    -		inter_name *iname = Responses::response_constant_iname(resp->responding_rule,
    -			resp->response_marker);
    +		inter_name *iname = Responses::response_constant_iname(resp->the_rule,
    +			resp->the_marker);
     		EmitCode::inv(IF_BIP);
     		EmitCode::down();
     			EmitCode::inv(EQ_BIP);
    @@ -346,7 +396,7 @@ say |R_14_RESP_B|, we print its current text, say response (B) for |R_14|.
     			EmitCode::down();
     				EmitCode::call(Hierarchy::find(RULEPRINTINGRULE_HL));
     				EmitCode::down();
    -					EmitCode::val_iname(K_value, RTRules::iname(resp->responding_rule));
    +					EmitCode::val_iname(K_value, RTRules::iname(resp->the_rule));
     				EmitCode::up();
     				EmitCode::inv(PRINT_BIP);
     				EmitCode::down();
    @@ -354,7 +404,7 @@ say |R_14_RESP_B|, we print its current text, say response (B) for |R_14|.
     				EmitCode::up();
     				EmitCode::inv(PRINTCHAR_BIP);
     				EmitCode::down();
    -					EmitCode::val_number((inter_ti) ('A' + resp->response_marker));
    +					EmitCode::val_number((inter_ti) ('A' + resp->the_marker));
     				EmitCode::up();
     				EmitCode::inv(PRINT_BIP);
     				EmitCode::down();
    @@ -366,7 +416,7 @@ say |R_14_RESP_B|, we print its current text, say response (B) for |R_14|.
     	Functions::end(save);
     
     @ The following array is used only by the testing command RESPONSES, and
    -enables the I6 template to print out all known responses at run-time,
    +enables the Inter template to print out all known responses at run-time,
     divided up by the extensions containing the rules which produce them.
     
     @ =
    @@ -414,144 +464,3 @@ divided up by the extensions containing the rules which produce them.
     		EmitArrays::numeric_entry((inter_ti) (tally-1));
     		contiguous_match = FALSE;
     	}
    -
    -@ =
    -stack_frame *Responses::frame_for_response(response_message *resp) {
    -	if (resp == NULL) return NULL;
    -	return resp->original_stack_frame;
    -}
    -
    -@ As mentioned above, assertions in the source text can change the text of
    -a given response even at compile time. But the rules code looks after that:
    -
    -=
    -void Responses::assert_response_value(rule *R, int marker, wording W) {
    -	Rules::now_rule_needs_response(R, marker, W);
    -}
    -
    -@ When we index a response, we also provide a paste button for the source
    -text to assert a change:
    -
    -=
    -void Responses::index_response(OUTPUT_STREAM, rule *R, int marker, response_message *resp) {
    -	WRITE("    ");
    -	HTML_OPEN_WITH("span",
    -		"style=\"color: #ffffff; "
    -		"font-family: 'Courier New', Courier, monospace; background-color: #8080ff;\"");
    -	WRITE("  %c   ", 'A' + marker);
    -	HTML_CLOSE("span");
    -	HTML_OPEN_WITH("span", "style=\"color: #000066;\"");
    -	WRITE("%+W", resp->original_text->unsubstituted_text);
    -	HTML_CLOSE("span");
    -	WRITE("  ");
    -	TEMPORARY_TEXT(S)
    -	WRITE_TO(S, "%+W response (%c)", R->name, 'A' + marker);
    -	PasteButtons::paste_text(OUT, S);
    -	WRITE(" name");
    -	WRITE(" ");
    -	Str::clear(S);
    -	WRITE_TO(S, "The %+W response (%c) is \"New text.\".");
    -	PasteButtons::paste_text(OUT, S);
    -	WRITE(" set");
    -	DISCARD_TEXT(S)
    -}
    -
    -@ =
    -int Responses::get_marker_from_response_spec(parse_node *rs) {
    -	if (Rvalues::is_CONSTANT_of_kind(rs, K_response)) {
    -		wording SW = Node::get_text(rs);
    -		if ((Wordings::length(SW) >= 2) && ((Wordings::one_word(Wordings::last_wn(SW)-1))))
    -			return <>;
    -	}
    -	return -1;
    -}
    -
    -@ To complete the code on strings, we just need the top-level routine which
    -handles the compilation of a general string literal. There are actually three
    -ways we might not even be compiling an I7 text value here:
    -
    -(a) If the specification is flagged "explicit", we're using this as a device
    -to hold low-level I6 property values such as |parse_name| routines, and we
    -simply compile the text raw.
    -(b) If we're in quotation mode, that means the text is destined to be in an
    -I6 "box" statement, which needs it to be formed in an eccentric way.
    -(c) If we're in bibliographic mode, we're compiling not to the I6 program
    -but to something like an XML description of its metadata, where again the
    -text needs to be printed in a particular way.
    -
    -=
    -void Responses::compile_general(value_holster *VH, parse_node *str) {
    -	wording SW = Node::get_text(str);
    -	if (Annotations::read_int(str, explicit_literal_ANNOT)) {
    -		if (Node::get_explicit_iname(str)) {
    -			if (Holsters::non_void_context(VH)) {
    -				Emit::holster_iname(VH, Node::get_explicit_iname(str));
    -			} else internal_error("unvalued SCG");
    -		} else {
    -			int A = Annotations::read_int(str, constant_number_ANNOT);
    -			if (Holsters::non_void_context(VH))
    -				Holsters::holster_pair(VH, LITERAL_IVAL, (inter_ti) A);
    -		}
    -	} else {
    -		if (Wordings::empty(SW)) internal_error("Text no longer available for CONSTANT/TEXT");
    -		@;
    -	}
    -}
    -
    -@ Responses take the form
    -= (text)
    -	"blah blah blah" ( letter )
    -=
    -so the penultimate word, if it's there, is the letter.
    -
    -@ =
    -	if ((Wordings::length(SW) >= 2) && ((Wordings::one_word(Wordings::last_wn(SW)-1))))
    -		@
    -	else @;
    -
    -@ =
    -	int code = <>;
    -	if ((rule_being_compiled == NULL) ||
    -		(Rules::rule_allows_responses(rule_being_compiled) == FALSE)) {
    -		StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_ResponseContextWrong),
    -			"lettered responses can only be used in named rules",
    -			"not in any of the other contexts in which quoted text can appear.");
    -		return;
    -	}
    -	if (Rules::get_response(rule_being_compiled, code)) {
    -		StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_ResponseDuplicated),
    -			"this duplicates a response letter",
    -			"which is not allowed: if a bracketed letter like (A) is used to mark "
    -			"some text as a response, then it can only occur once in its rule.");
    -		return;
    -	}
    -	stack_frame *frame = Frames::current_stack_frame();
    -	if (Holsters::non_void_context(VH)) {
    -		int downs = LocalParking::park(frame);
    -		response_message *resp =
    -			Responses::response_cue(VH, rule_being_compiled, code, SW,
    -				Frames::boxed_frame(frame), FALSE);
    -		Rules::set_response(rule_being_compiled, code, resp);
    -		while (downs > 0) { EmitCode::up(); downs--; }
    -	}
    -
    -@ =
    -	if (Annotations::read_int(str, text_unescaped_ANNOT)) {
    -		if (CompileValues::compiling_in_constant_mode()) {
    -			inter_name *val_iname = TextLiterals::to_value_unescaped(SW);
    -			Emit::holster_iname(VH, val_iname);
    -		} else {
    -			inter_name *val_iname = TextLiterals::to_value_unescaped(SW);
    -			Emit::holster_iname(VH, val_iname);
    -		}
    -	} else if (Vocabulary::test_flags(Wordings::first_wn(SW), TEXTWITHSUBS_MC)) {
    -		TextSubstitutions::text_substitution_cue(VH, SW);
    -	} else {
    -		if (CompileValues::compiling_in_constant_mode()) {
    -			inter_name *val_iname = TextLiterals::to_value(SW);
    -			Emit::holster_iname(VH, val_iname);
    -		} else {
    -			inter_name *val_iname = TextLiterals::to_value(SW);
    -			Emit::holster_iname(VH, val_iname);
    -		}
    -	}
    diff --git a/inform7/runtime-module/Chapter 4/Text Literals.w b/inform7/runtime-module/Chapter 4/Text Literals.w
    index 58bc16515..0f07c41cb 100644
    --- a/inform7/runtime-module/Chapter 4/Text Literals.w	
    +++ b/inform7/runtime-module/Chapter 4/Text Literals.w	
    @@ -35,11 +35,15 @@ it will always be a function.
     =
     inter_name *TextLiterals::small_block(inter_name *content) {
     	inter_name *small_block = Enclosures::new_small_block_for_constant();
    -	packaging_state save = EmitArrays::begin(small_block, K_value);
    +	TextLiterals::compile_value_to(small_block, content);
    +	return small_block;
    +}
    +
    +void TextLiterals::compile_value_to(inter_name *at, inter_name *content) {
    +	packaging_state save = EmitArrays::begin(at, K_value);
     	EmitArrays::iname_entry(Hierarchy::find(CONSTANT_PACKED_TEXT_STORAGE_HL));
     	EmitArrays::iname_entry(content);
     	EmitArrays::end(save);
    -	return small_block;
     }
     
     @h Default value.
    diff --git a/inform7/runtime-module/Chapter 4/Text Substitutions.w b/inform7/runtime-module/Chapter 4/Text Substitutions.w
    index 1ecc7d3b3..90af23eed 100644
    --- a/inform7/runtime-module/Chapter 4/Text Substitutions.w	
    +++ b/inform7/runtime-module/Chapter 4/Text Substitutions.w	
    @@ -130,7 +130,6 @@ typedef struct text_substitution {
     	struct stack_frame *using_frame; /* for cases where possible */
     
     	struct inter_name *ts_value_iname; /* the I6 array for this */
    -	int ts_value_iname_used;
     	struct inter_name *ts_function_iname; /* the routine to implement it */
     
     	struct rule *responding_to_rule;
    @@ -145,13 +144,9 @@ typedef struct text_substitution {
     	value ----------------> CONSTANT_PACKED_TEXT_STORAGE or CONSTANT_PERISHABLE_TEXT_STORAGE
     	                        function ----------------------> ...
     =
    -While the function is always compiled, the small block is only compiled if
    -the value iname is ever actually requested. (Sometimes when making alternative
    -responses we just want to make the function.)
     
     =
     inter_name *TextSubstitutions::value_iname(text_substitution *ts) {
    -	ts->ts_value_iname_used = TRUE;
     	return ts->ts_value_iname;
     }
     
    @@ -159,7 +154,8 @@ inter_name *TextSubstitutions::function_iname(text_substitution *ts) {
     	return ts->ts_function_iname;
     }
     
    -@ 
    +@ Note that this function is called both when cues are detected (above), and
    +also when responses are created -- see //Responses//.
     
     =
     text_substitution *TextSubstitutions::new_text_substitution(wording W,
    @@ -170,9 +166,8 @@ text_substitution *TextSubstitutions::new_text_substitution(wording W,
     		internal_error("Too late for further text substitutions");
     	ts->unsubstituted_text = Wordings::first_word(W);
     	ts->sentence_using_this = current_sentence;
    -	if (R) {
    -		ts->using_frame = NULL;
    -	} else {
    +	ts->using_frame = NULL;
    +	if (R == NULL) {
     		stack_frame new_frame = Frames::new();
     		ts->using_frame = Frames::boxed_frame(&new_frame);
     		if (frame) LocalVariableSlates::append(ts->using_frame, frame);
    @@ -190,7 +185,6 @@ text_substitution *TextSubstitutions::new_text_substitution(wording W,
     	if (R) P = RTRules::package(R);
     	package_request *PR = Hierarchy::package_within(LITERALS_HAP, P);
     	ts->ts_value_iname = Hierarchy::make_iname_in(TEXT_SUBSTITUTION_HL, PR);
    -	ts->ts_value_iname_used = FALSE;
     	ts->ts_function_iname = Hierarchy::make_iname_in(TEXT_SUBSTITUTION_FN_HL, PR);
     
     	ts->owning_point = current_sentence;
    @@ -202,16 +196,13 @@ text_substitution *TextSubstitutions::new_text_substitution(wording W,
     }
     
     @h Compilation.
    +Functions for substitutions are then compiled in due course by the following coroutine
    +(see //core: How To Compile//):
     
     =
    -int TextSubstitutions::compilation_coroutine(void) {
    -	return TextSubstitutions::compile_as_needed(FALSE);
    -}
    -
     text_substitution *latest_ts_compiled = NULL;
     int compiling_text_routines_mode = FALSE; /* used for better problem messages */
    -int TextSubstitutions::compile_as_needed(int in_response_mode) {
    -	Responses::compile_response_launchers();
    +int TextSubstitutions::compilation_coroutine(void) {
     	int N = 0;
     	compiling_text_routines_mode = TRUE;
     	while (TRUE) {
    @@ -220,13 +211,11 @@ int TextSubstitutions::compile_as_needed(int in_response_mode) {
     		else ts = NEXT_OBJECT(latest_ts_compiled, text_substitution);
     		if (ts == NULL) break;
     		latest_ts_compiled = ts;
    -		int responding = FALSE;
    -		if (ts->responding_to_rule) responding = TRUE;
    -		if ((responding == in_response_mode) && (ts->tr_done_already == FALSE)) {
    +		if (ts->tr_done_already == FALSE) {
     			ts->tr_done_already = TRUE;
     			int makes_local_refs = TextSubstitutions::compile_function(ts);
    -			if (ts->ts_value_iname_used)
    -				TextSubstitutions::compile_value(ts->ts_value_iname, ts->ts_function_iname, makes_local_refs);
    +			TextSubstitutions::compile_value(ts->ts_value_iname,
    +				ts->ts_function_iname, makes_local_refs);
     		}
     		N++;
     	}
    @@ -252,34 +241,30 @@ int TextSubstitutions::compile_function(text_substitution *ts) {
     
     	current_ts_being_compiled = ts;
     	packaging_state save = Functions::begin(ts->ts_function_iname);
    -	stack_frame *frame = ts->using_frame;
    -	if ((ts->responding_to_rule) && (ts->responding_to_marker >= 0)) {
    -		response_message *resp = Rules::get_response(
    -			ts->responding_to_rule, ts->responding_to_marker);
    -		if (resp) frame = Responses::frame_for_response(resp);
    -	}
    -	if (frame) LocalVariableSlates::append(Frames::current_stack_frame(), frame);
    +	stack_frame *frame = NULL;
    +	@;
    +
     	LocalVariables::monitor_local_parsing(Frames::current_stack_frame());
    -
    +	@;
     	@;
    -
     	int makes_local_references =
     		LocalVariables::local_parsed_recently(Frames::current_stack_frame());
    -	if (makes_local_references) {
    -		Produce::push_code_position(Emit::tree(), Produce::begin_position(Emit::tree()), Inter::Bookmarks::snapshot(Emit::at()));
    -		LocalParking::retrieve(frame);
    -		Produce::pop_code_position(Emit::tree());
    -	}
    +	if (makes_local_references) @;
    +
     	Functions::end(save);
     	current_ts_being_compiled = NULL;
     	return makes_local_references;
     }
     
    -@ Of course, if we used Inform's standard phrase mechanism exactly, then
    -the whole thing would be circular, because that would once again generate
    -a request for a new text substitution to be compiled later...
    +@ =
    +	frame = Responses::frame_for_response(ts->responding_to_rule, ts->responding_to_marker);
    +	if (frame == NULL) frame = ts->using_frame;
    +	if (frame) LocalVariableSlates::append(Frames::current_stack_frame(), frame);
     
    -@ =
    +@ In DEBUG mode, there's an option to print the unsubstituted text instead --
    +note the |rtrue| here, which stops the function from proceeding.
    +
    +@ =
     	if (TargetVMs::debug_enabled(Task::vm())) {
     		EmitCode::inv(IFDEBUG_BIP);
     		EmitCode::down();
    @@ -287,7 +272,8 @@ a request for a new text substitution to be compiled later...
     			EmitCode::down();
     				EmitCode::inv(IF_BIP);
     				EmitCode::down();
    -					EmitCode::val_iname(K_number, Hierarchy::find(SUPPRESS_TEXT_SUBSTITUTION_HL));
    +					EmitCode::val_iname(K_number,
    +						Hierarchy::find(SUPPRESS_TEXT_SUBSTITUTION_HL));
     					EmitCode::code();
     					EmitCode::down();
     						EmitCode::inv(PRINT_BIP);
    @@ -304,6 +290,11 @@ a request for a new text substitution to be compiled later...
     		EmitCode::up();
     	}
     
    +@ Of course, if we used Inform's standard phrase mechanism exactly, then
    +the whole thing would be circular, because that would once again generate
    +a request for a new text substitution to be compiled later...
    +
    +@ =
     	parse_node *ts_code_block = Node::new(IMPERATIVE_NT);
     	CompilationUnits::assign_to_same_unit(ts_code_block, ts->owning_point);
     	ts_code_block->next = Node::new(UNKNOWN_NT);
    @@ -315,17 +306,17 @@ a request for a new text substitution to be compiled later...
     
     	EmitCode::rtrue();
     
    -@ See the "Responses" section for why, but we sometimes want to force
    -the coroutine to go through the whole queue once, then go back to the
    -start again -- which would be very inefficient except that in this mode
    -we aren't doing very much; most TSs will be passed quickly over.
    +@ Where a text substitution refers to local variables in the caller,
    +//imperative: Local Parking// is used to pass it the current values of those
    +locals; and this means that the function must begin by retrieving those values.
    +But since we have already compiled most of the function, we have to go back to
    +the start temporarily to insert this extra code.
     
    -=
    -void TextSubstitutions::compile_text_routines_in_response_mode(void) {
    -	latest_ts_compiled = NULL;
    -	TextSubstitutions::compile_as_needed(TRUE);
    -	latest_ts_compiled = NULL;
    -}
    +@ =
    +	Produce::push_code_position(Emit::tree(),
    +		Produce::begin_position(Emit::tree()), Inter::Bookmarks::snapshot(Emit::at()));
    +	LocalParking::retrieve(frame);
    +	Produce::pop_code_position(Emit::tree());
     
     @h It may be worth adding.
     Finally, the following clarifies problem messages arising from the issue of
    diff --git a/inform7/values-module/Chapter 2/Rvalues.w b/inform7/values-module/Chapter 2/Rvalues.w
    index 152f5dde6..89adc7b7a 100644
    --- a/inform7/values-module/Chapter 2/Rvalues.w	
    +++ b/inform7/values-module/Chapter 2/Rvalues.w	
    @@ -76,6 +76,15 @@ property *Rvalues::to_property(parse_node *spec) {
     		CONV_TO(property) }
     rule *Rvalues::to_rule(parse_node *spec) { 
     		CONV_TO(rule) }
    +int Rvalues::to_response_marker(parse_node *spec) { 
    +	if (Rvalues::is_CONSTANT_of_kind(spec, K_response)) {
    +		wording SW = Node::get_text(spec);
    +		if ((Wordings::length(SW) >= 2) &&
    +			((Wordings::one_word(Wordings::last_wn(SW)-1))))
    +			return <>;
    +	}
    +	return -1;
    +}
     rulebook *Rvalues::to_rulebook(parse_node *spec) { 
     		CONV_TO(rulebook) }
     table *Rvalues::to_table(parse_node *spec) {