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); ++
§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(" "); + 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(" <i>name</i>"); + WRITE(" "); + Str::clear(S); + WRITE_TO(S, "The %+W response (%c) is \"New text.\"."); + PasteButtons::paste_text(OUT, S); + WRITE(" <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.
-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:
-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. +
-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 @@ callsHACK_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)); }--
- This code is used in §6.3.1.
§6.3.1.2. End a possible run of matches6.3.1.2 = +
+
- This code is used in §7.2.1.
§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).
-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(" "); - 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(" <i>name</i>"); - WRITE(" "); - Str::clear(S); - WRITE_TO(S, "The %+W response (%c) is \"New text.\"."); - PasteButtons::paste_text(OUT, S); - WRITE(" <i>set</i>"); - 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) && (<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--; } - } ---
- This code is used in §11.1.
§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 §11.1.
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.
- This code is used in §7.2.1 (twice).
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) {
- Home
- Inform7
- runtime
- Chapter 4: Enclosed Resources
- Text Substitutions
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. 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. 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); ++
§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(); } ++
§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();
§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 +
§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_provisoint 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; }- +-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) {