1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-08 18:14:21 +03:00
inform7/docs/core-module/17-ts.html
2019-08-31 13:56:36 +01:00

478 lines
55 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>17/tl</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Language" content="en-gb">
<link href="inweb.css" rel="stylesheet" rev="stylesheet" type="text/css">
</head>
<body>
<!--Weave of '17/ts' generated by 7-->
<ul class="crumbs"><li><a href="../webs.html">&#9733;</a></li><li><a href="index.html">core</a></li><li><a href="index.html#17">Chapter 17: Text Data</a></li><li><b>Text Substitutions</b></li></ul><p class="purpose">In this section we compile text with substitutions.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. Definitions</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. Definitions. </b></p>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. </b>Text containing substitutions, such as "You pick up [the noun] thoughtfully.",
are compiled as routines rather than Z-machine strings. Each is stored in one
of the following structures. Unlike literal text, a text routine might lead
to problem messages when eventually compiled, so it is useful to record the
current sentence when a text routine is created: this means a problem can
be reported at the right place.
</p>
<p class="inwebparagraph">The easiest way to understand this section is to pretend that responses
don't exist, and ignore them until they come up later.
</p>
<pre class="display">
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">text_substitution</span><span class="plain"> {</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="identifier">wording</span><span class="plain"> </span><span class="identifier">unsubstituted_text</span><span class="plain">; </span> <span class="comment">including the substitutions in squares</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">dont_need_after_all</span><span class="plain">; </span> <span class="comment">in case replaced as a response</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">tr_done_already</span><span class="plain">; </span> <span class="comment">has been compiled</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">rule</span><span class="plain"> *</span><span class="identifier">responding_to_rule</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">responding_to_marker</span><span class="plain">;</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="identifier">parse_node</span><span class="plain"> *</span><span class="identifier">sentence_using_this</span><span class="plain">; </span> <span class="comment">where this occurs in source</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">local_names_existed_at_usage_time</span><span class="plain">; </span> <span class="comment">remember in case of problems</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">ph_stack_frame</span><span class="plain"> *</span><span class="identifier">parked_stack_frame</span><span class="plain">; </span> <span class="comment">for cases where possible</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="identifier">inter_name</span><span class="plain"> *</span><span class="identifier">ts_iname</span><span class="plain">; </span> <span class="comment">the I6 array for this</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="identifier">inter_name</span><span class="plain"> *</span><span class="identifier">ts_routine_iname</span><span class="plain">; </span> <span class="comment">the routine to implement it</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">ts_sb_needed</span><span class="plain">; </span> <span class="comment">reference copy of small block needed as a constant?</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="identifier">compilation_module</span><span class="plain"> *</span><span class="identifier">belongs_to_module</span><span class="plain">;</span>
<span class="identifier">MEMORY_MANAGEMENT</span>
<span class="plain">} </span><span class="reserved">text_substitution</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure text_substitution is accessed in 17/rs and here.</p>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. </b>We are only allowed to create new ones until the following is set:
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">no_further_text_subs</span><span class="plain"> = </span><span class="identifier">FALSE</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. </b>The following global variable records whether we are currently compiling
a text routine, rather than some other routine, or free-standing objects.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">compiling_text_routines_mode</span><span class="plain"> = </span><span class="identifier">FALSE</span><span class="plain">; </span> <span class="comment">used for better problem messages</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. </b>Like literal texts, text substitutions aren't printed out in full when
they first arise; we keep a note of them when we need them, and compile
suitable routines later.
</p>
<p class="inwebparagraph">A problem with this is that a text substitution probably contains references
to variables which exist now, but may not exist later when a routine to do
the printing is being compiled. For example,
</p>
<blockquote>
<p>say "The dial reads [counter].";</p>
</blockquote>
<p class="inwebparagraph">may do quite different things at different points in the code, according to
what <code class="display"><span class="extract">counter</span></code> currently means. So we need to take note of the current
stack frame; and we mustn't optimise by compiling identical text substitutions
to the same routines to print them.
</p>
<pre class="display">
<span class="reserved">text_substitution</span><span class="plain"> *</span><span class="functiontext">Strings::TextSubstitutions::new_text_substitution</span><span class="plain">(</span><span class="identifier">wording</span><span class="plain"> </span><span class="identifier">W</span><span class="plain">,</span>
<span class="reserved">ph_stack_frame</span><span class="plain"> *</span><span class="identifier">phsf</span><span class="plain">, </span><span class="reserved">rule</span><span class="plain"> *</span><span class="identifier">R</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">marker</span><span class="plain">, </span><span class="identifier">package_request</span><span class="plain"> *</span><span class="identifier">P</span><span class="plain">) {</span>
<span class="reserved">text_substitution</span><span class="plain"> *</span><span class="identifier">ts</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">text_substitution</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">no_further_text_subs</span><span class="plain">) </span>&lt;<span class="cwebmacro">Panic, because it is really too late</span> <span class="cwebmacronumber">5.1</span>&gt;<span class="plain">;</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;unsubstituted_text</span><span class="plain"> = </span><span class="identifier">Wordings::first_word</span><span class="plain">(</span><span class="identifier">W</span><span class="plain">);</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;sentence_using_this</span><span class="plain"> = </span><span class="identifier">current_sentence</span><span class="plain">;</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;local_names_existed_at_usage_time</span><span class="plain"> = </span><span class="identifier">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">R</span><span class="plain">) {</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;parked_stack_frame</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">ph_stack_frame</span><span class="plain"> </span><span class="identifier">new_frame</span><span class="plain"> = </span><span class="functiontext">Frames::new</span><span class="plain">();</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;parked_stack_frame</span><span class="plain"> = </span><span class="functiontext">Frames::boxed_frame</span><span class="plain">(&amp;</span><span class="identifier">new_frame</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">phsf</span><span class="plain">) </span><span class="functiontext">LocalVariables::copy</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;parked_stack_frame</span><span class="plain">, </span><span class="identifier">phsf</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;responding_to_rule</span><span class="plain"> = </span><span class="identifier">R</span><span class="plain">;</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;responding_to_marker</span><span class="plain"> = </span><span class="identifier">marker</span><span class="plain">;</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;dont_need_after_all</span><span class="plain"> = </span><span class="identifier">FALSE</span><span class="plain">;</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;tr_done_already</span><span class="plain"> = </span><span class="identifier">FALSE</span><span class="plain">;</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_sb_needed</span><span class="plain"> = </span><span class="identifier">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">phrase_being_compiled</span><span class="plain">) &amp;&amp;</span>
<span class="plain">(</span><span class="functiontext">LocalVariables::count</span><span class="plain">(</span><span class="functiontext">Frames::current_stack_frame</span><span class="plain">()) &gt; 0))</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;local_names_existed_at_usage_time</span><span class="plain"> = </span><span class="identifier">TRUE</span><span class="plain">;</span>
<span class="identifier">package_request</span><span class="plain"> *</span><span class="identifier">PR</span><span class="plain"> = </span><span class="functiontext">Hierarchy::package_within</span><span class="plain">(</span><span class="constant">LITERALS_HAP</span><span class="plain">, </span><span class="identifier">P</span><span class="plain">);</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_iname</span><span class="plain"> = </span><span class="functiontext">Hierarchy::make_iname_in</span><span class="plain">(</span><span class="constant">TEXT_SUBSTITUTION_HL</span><span class="plain">, </span><span class="identifier">PR</span><span class="plain">);</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_routine_iname</span><span class="plain"> = </span><span class="functiontext">Hierarchy::make_iname_in</span><span class="plain">(</span><span class="constant">TEXT_SUBSTITUTION_FN_HL</span><span class="plain">, </span><span class="identifier">PR</span><span class="plain">);</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;belongs_to_module</span><span class="plain"> = </span><span class="functiontext">Modules::current</span><span class="plain">();</span>
<span class="identifier">LOGIF</span><span class="plain">(</span><span class="identifier">TEXT_SUBSTITUTIONS</span><span class="plain">, </span><span class="string">"Requesting text routine %d %08x %W %08x\</span><span class="plain">n</span><span class="string">"</span><span class="plain">,</span>
<span class="identifier">ts</span><span class="plain">-&gt;</span><span class="identifier">allocation_id</span><span class="plain">, (</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">phsf</span><span class="plain">, </span><span class="identifier">W</span><span class="plain">, </span><span class="identifier">R</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">ts</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Strings::TextSubstitutions::new_text_substitution is used in <a href="#SP8">&#167;8</a>, <a href="#SP8_1">&#167;8.1</a>, 17/rs (<a href="17-rs.html#SP5">&#167;5</a>, <a href="17-rs.html#SP7_1">&#167;7.1</a>).</p>
<p class="inwebparagraph"><a id="SP5_1"></a><b>&#167;5.1. </b>Timing is going to turn out to be a real problem in all of this code.
If Inform finds that it needs a text substitution very late in its run &mdash;
after it has compiled them and can't compile any more &mdash; there's nothing
to do but panic.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Panic, because it is really too late</span> <span class="cwebmacronumber">5.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"Too late for further text substitutions"</span><span class="plain">);</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP5">&#167;5</a>.</p>
<p class="inwebparagraph"><a id="SP6"></a><b>&#167;6. </b>The template layer calls the following when that midnight hour chimes:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Strings::TextSubstitutions::allow_no_further_text_subs</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="identifier">no_further_text_subs</span><span class="plain"> = </span><span class="identifier">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Strings::TextSubstitutions::allow_no_further_text_subs is used in 1/mr (<a href="1-mr.html#SP4_14">&#167;4.14</a>).</p>
<p class="inwebparagraph"><a id="SP7"></a><b>&#167;7. </b>For some years these were compiled to routines verbosely called
<code class="display"><span class="extract">text_routine_1</span></code> and so on, but no longer:
</p>
<pre class="display">
<span class="identifier">inter_name</span><span class="plain"> *</span><span class="functiontext">Strings::TextSubstitutions::text_substitution_iname</span><span class="plain">(</span><span class="reserved">text_substitution</span><span class="plain"> *</span><span class="identifier">ts</span><span class="plain">) {</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_sb_needed</span><span class="plain"> = </span><span class="identifier">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_iname</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Strings::TextSubstitutions::text_substitution_iname is used in <a href="#SP8">&#167;8</a>, <a href="#SP8_1">&#167;8.1</a>, 17/rs (<a href="17-rs.html#SP7_1">&#167;7.1</a>).</p>
<p class="inwebparagraph"><a id="SP8"></a><b>&#167;8. </b>The following is called when we want to compile a usage of a text
substitution; for instance, when compiling
</p>
<blockquote>
<p>say "The time is [time of day]. Hurry!";</p>
</blockquote>
<p class="inwebparagraph">we'll compile a call to a routine like <code class="display"><span class="extract">TS_1()</span></code>, and make a note to compile
that routine later. This appearance of the routine name is called the "cue".
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Strings::TextSubstitutions::text_substitution_cue</span><span class="plain">(</span><span class="identifier">value_holster</span><span class="plain"> *</span><span class="identifier">VH</span><span class="plain">, </span><span class="identifier">wording</span><span class="plain"> </span><span class="identifier">W</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">adopted_rule_for_compilation</span><span class="plain">) {</span>
<span class="functiontext">Rules::log</span><span class="plain">(</span><span class="identifier">adopted_rule_for_compilation</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">ph_stack_frame</span><span class="plain"> *</span><span class="identifier">phsf</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">adopted_rule_for_compilation</span><span class="plain">) {</span>
&lt;<span class="cwebmacro">Write the actual cue</span> <span class="cwebmacronumber">8.1</span>&gt;<span class="plain">;</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">Holsters::data_acceptable</span><span class="plain">(</span><span class="identifier">VH</span><span class="plain">)) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">downs</span><span class="plain"> = 0;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">TEST_COMPILATION_MODE</span><span class="plain">(</span><span class="constant">PERMIT_LOCALS_IN_TEXT_CMODE</span><span class="plain">)) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">phsf</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">phsf</span><span class="plain"> = </span><span class="functiontext">Frames::current_stack_frame</span><span class="plain">();</span>
<span class="identifier">downs</span><span class="plain"> = </span><span class="functiontext">LocalVariables::emit_storage</span><span class="plain">(</span><span class="identifier">phsf</span><span class="plain">);</span>
<span class="identifier">phsf</span><span class="plain"> = </span><span class="functiontext">Frames::boxed_frame</span><span class="plain">(</span><span class="identifier">phsf</span><span class="plain">);</span>
<span class="identifier">Produce::inv_call_iname</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">(), </span><span class="functiontext">Hierarchy::find</span><span class="plain">(</span><span class="constant">TEXT_TY_EXPANDIFPERISHABLE_HL</span><span class="plain">));</span>
<span class="identifier">Produce::down</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="functiontext">Frames::emit_allocation</span><span class="plain">(</span><span class="identifier">K_text</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">text_substitution</span><span class="plain"> *</span><span class="identifier">ts</span><span class="plain"> = </span><span class="functiontext">Strings::TextSubstitutions::new_text_substitution</span><span class="plain">(</span><span class="identifier">W</span><span class="plain">, </span><span class="identifier">phsf</span><span class="plain">,</span>
<span class="identifier">adopted_rule_for_compilation</span><span class="plain">, </span><span class="identifier">adopted_marker_for_compilation</span><span class="plain">, </span><span class="functiontext">Emit::current_enclosure</span><span class="plain">());</span>
<span class="identifier">inter_name</span><span class="plain"> *</span><span class="identifier">tin</span><span class="plain"> = </span><span class="functiontext">Strings::TextSubstitutions::text_substitution_iname</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">VH</span><span class="plain">-&gt;</span><span class="identifier">vhmode_wanted</span><span class="plain"> == </span><span class="identifier">INTER_DATA_VHMODE</span><span class="plain">)</span>
<span class="functiontext">Emit::holster</span><span class="plain">(</span><span class="identifier">VH</span><span class="plain">, </span><span class="identifier">tin</span><span class="plain">);</span>
<span class="reserved">else</span>
<span class="identifier">Produce::val_iname</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">(), </span><span class="identifier">K_value</span><span class="plain">, </span><span class="identifier">tin</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">TEST_COMPILATION_MODE</span><span class="plain">(</span><span class="constant">PERMIT_LOCALS_IN_TEXT_CMODE</span><span class="plain">)) {</span>
<span class="identifier">Produce::up</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">downs</span><span class="plain"> &gt; 0) { </span><span class="identifier">Produce::up</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">()); </span><span class="identifier">downs</span><span class="plain">--; }</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Strings::TextSubstitutions::text_substitution_cue is used in 17/rs (<a href="17-rs.html#SP12_1_2">&#167;12.1.2</a>).</p>
<p class="inwebparagraph"><a id="SP8_1"></a><b>&#167;8.1. </b><code class="display">
&lt;<span class="cwebmacrodefn">Write the actual cue</span> <span class="cwebmacronumber">8.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">text_substitution</span><span class="plain"> *</span><span class="identifier">ts</span><span class="plain"> = </span><span class="functiontext">Strings::TextSubstitutions::new_text_substitution</span><span class="plain">(</span><span class="identifier">W</span><span class="plain">, </span><span class="identifier">phsf</span><span class="plain">,</span>
<span class="identifier">adopted_rule_for_compilation</span><span class="plain">, </span><span class="identifier">adopted_marker_for_compilation</span><span class="plain">, </span><span class="functiontext">Emit::current_enclosure</span><span class="plain">());</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">TEST_COMPILATION_MODE</span><span class="plain">(</span><span class="constant">CONSTANT_CMODE</span><span class="plain">)) {</span>
<span class="identifier">package_request</span><span class="plain"> *</span><span class="identifier">PR</span><span class="plain"> = </span><span class="functiontext">Hierarchy::package_in_enclosure</span><span class="plain">(</span><span class="constant">BLOCK_CONSTANTS_HAP</span><span class="plain">);</span>
<span class="identifier">inter_name</span><span class="plain"> *</span><span class="identifier">N</span><span class="plain"> = </span><span class="functiontext">Hierarchy::make_iname_in</span><span class="plain">(</span><span class="constant">BLOCK_CONSTANT_HL</span><span class="plain">, </span><span class="identifier">PR</span><span class="plain">);</span>
<span class="identifier">packaging_state</span><span class="plain"> </span><span class="identifier">save</span><span class="plain"> = </span><span class="functiontext">Emit::named_late_array_begin</span><span class="plain">(</span><span class="identifier">N</span><span class="plain">, </span><span class="identifier">K_value</span><span class="plain">);</span>
<span class="functiontext">Emit::array_iname_entry</span><span class="plain">(</span><span class="functiontext">Hierarchy::find</span><span class="plain">(</span><span class="constant">CONSTANT_PACKED_TEXT_STORAGE_HL</span><span class="plain">));</span>
<span class="functiontext">Emit::array_iname_entry</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_routine_iname</span><span class="plain">);</span>
<span class="functiontext">Emit::array_end</span><span class="plain">(</span><span class="identifier">save</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">N</span><span class="plain">) </span><span class="functiontext">Emit::holster</span><span class="plain">(</span><span class="identifier">VH</span><span class="plain">, </span><span class="identifier">N</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">inter_name</span><span class="plain"> *</span><span class="identifier">tin</span><span class="plain"> = </span><span class="functiontext">Strings::TextSubstitutions::text_substitution_iname</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">Holsters::data_acceptable</span><span class="plain">(</span><span class="identifier">VH</span><span class="plain">)) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">tin</span><span class="plain">) </span><span class="functiontext">Emit::holster</span><span class="plain">(</span><span class="identifier">VH</span><span class="plain">, </span><span class="identifier">tin</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP8">&#167;8</a>.</p>
<p class="inwebparagraph"><a id="SP9"></a><b>&#167;9. </b>And the following clarifies problem messages arising from this point,
since it often confuses newcomers:
</p>
<pre class="display">
<span class="reserved">text_substitution</span><span class="plain"> *</span><span class="identifier">current_ts_being_compiled</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Strings::TextSubstitutions::append_text_substitution_proviso</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">it_is_not_worth_adding</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">current_ts_being_compiled</span><span class="plain">) &amp;&amp;</span>
<span class="plain">(</span><span class="identifier">current_ts_being_compiled</span><span class="plain">-</span><span class="element">&gt;local_names_existed_at_usage_time</span><span class="plain">)) {</span>
<span class="functiontext">Frames::log</span><span class="plain">(</span><span class="functiontext">Frames::current_stack_frame</span><span class="plain">());</span>
<span class="identifier">Problems::quote_wording</span><span class="plain">(9, </span><span class="identifier">current_ts_being_compiled</span><span class="plain">-</span><span class="element">&gt;unsubstituted_text</span><span class="plain">);</span>
<span class="identifier">Problems::issue_problem_segment</span><span class="plain">(</span>
<span class="string">" %PIt may be worth adding that this problem arose in text "</span>
<span class="string">"which both contains substitutions and is also being used as "</span>
<span class="string">"a value - being put into a variable, or used as one of the "</span>
<span class="string">"ingredients in a phrase other than 'say'. Because that means "</span>
<span class="string">"it needs to be used in places outside its immediate context, "</span>
<span class="string">"it is not allowed to refer to any 'let' values or phrase "</span>
<span class="string">"options - those are temporary things, long gone by the time it "</span>
<span class="string">"would need to be printed."</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Strings::TextSubstitutions::append_text_substitution_proviso appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP10"></a><b>&#167;10. </b>So much for the cues. As with text literals in the previous section, it's
now time to redeem our promises and compile the <code class="display"><span class="extract">TS_X</span></code> routines. These routines
can't be produced all at once, and are sometimes not needed at all: the
responses mechanism makes this quite fiddly, and so do the existence of
other constructs in Inform which, when compiled, may make new text
substitutions. So compilation is handled by a coroutine. (I'm a
little old-fashioned in calling this a coroutine: it achieves its task in
instalments, effectively sharing time with other routines which in turn
add to its task, until everybody is done.)
</p>
<p class="inwebparagraph">Basically, we compile as many text substitutions as we can out of those not
yet done, returning the number we compile.
</p>
<pre class="display">
<span class="reserved">text_substitution</span><span class="plain"> *</span><span class="identifier">latest_ts_compiled</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Strings::TextSubstitutions::compilation_coroutine</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">in_response_mode</span><span class="plain">) {</span>
<span class="functiontext">Strings::compile_response_launchers</span><span class="plain">();</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">N</span><span class="plain"> = 0;</span>
<span class="identifier">compiling_text_routines_mode</span><span class="plain"> = </span><span class="identifier">TRUE</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">TRUE</span><span class="plain">) {</span>
<span class="reserved">text_substitution</span><span class="plain"> *</span><span class="identifier">ts</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">latest_ts_compiled</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">ts</span><span class="plain"> = </span><span class="identifier">FIRST_OBJECT</span><span class="plain">(</span><span class="reserved">text_substitution</span><span class="plain">);</span>
<span class="reserved">else</span><span class="plain"> </span><span class="identifier">ts</span><span class="plain"> = </span><span class="identifier">NEXT_OBJECT</span><span class="plain">(</span><span class="identifier">latest_ts_compiled</span><span class="plain">, </span><span class="reserved">text_substitution</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">ts</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">latest_ts_compiled</span><span class="plain"> = </span><span class="identifier">ts</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">responding</span><span class="plain"> = </span><span class="identifier">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;responding_to_rule</span><span class="plain">) </span><span class="identifier">responding</span><span class="plain"> = </span><span class="identifier">TRUE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;dont_need_after_all</span><span class="plain"> == </span><span class="identifier">FALSE</span><span class="plain">) &amp;&amp; (</span><span class="identifier">responding</span><span class="plain"> == </span><span class="identifier">in_response_mode</span><span class="plain">) &amp;&amp;</span>
<span class="plain">(</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;tr_done_already</span><span class="plain"> == </span><span class="identifier">FALSE</span><span class="plain">)) {</span>
<span class="functiontext">Strings::TextSubstitutions::compile_single_substitution</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">N</span><span class="plain">++;</span>
<span class="plain">}</span>
<span class="identifier">compiling_text_routines_mode</span><span class="plain"> = </span><span class="identifier">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">N</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Strings::TextSubstitutions::compilation_coroutine is used in <a href="#SP12">&#167;12</a>, 22/cs (<a href="22-cs.html#SP14">&#167;14</a>).</p>
<p class="inwebparagraph"><a id="SP11"></a><b>&#167;11. </b>We can now forget about the coroutine management, and just compile a single
text substitution. The main thing is to copy over references to local variables
from the stack frame creating this text substitution to the stack frame
compiling it.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Strings::TextSubstitutions::compile_single_substitution</span><span class="plain">(</span><span class="reserved">text_substitution</span><span class="plain"> *</span><span class="identifier">ts</span><span class="plain">) {</span>
<span class="identifier">LOGIF</span><span class="plain">(</span><span class="identifier">TEXT_SUBSTITUTIONS</span><span class="plain">, </span><span class="string">"Compiling text routine %d %08x %W\</span><span class="plain">n</span><span class="string">"</span><span class="plain">,</span>
<span class="identifier">ts</span><span class="plain">-&gt;</span><span class="identifier">allocation_id</span><span class="plain">, (</span><span class="reserved">int</span><span class="plain">) (</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;parked_stack_frame</span><span class="plain">), </span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;unsubstituted_text</span><span class="plain">);</span>
<span class="identifier">current_ts_being_compiled</span><span class="plain"> = </span><span class="identifier">ts</span><span class="plain">;</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;tr_done_already</span><span class="plain"> = </span><span class="identifier">TRUE</span><span class="plain">;</span>
<span class="identifier">packaging_state</span><span class="plain"> </span><span class="identifier">save</span><span class="plain"> = </span><span class="functiontext">Routines::begin</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_routine_iname</span><span class="plain">);</span>
<span class="reserved">ph_stack_frame</span><span class="plain"> *</span><span class="identifier">phsf</span><span class="plain"> = </span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;parked_stack_frame</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;responding_to_rule</span><span class="plain">) &amp;&amp; (</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;responding_to_marker</span><span class="plain"> &gt;= 0)) {</span>
<span class="reserved">response_message</span><span class="plain"> *</span><span class="identifier">resp</span><span class="plain"> = </span><span class="functiontext">Rules::rule_defines_response</span><span class="plain">(</span>
<span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;responding_to_rule</span><span class="plain">, </span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;responding_to_marker</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">resp</span><span class="plain">) </span><span class="identifier">phsf</span><span class="plain"> = </span><span class="functiontext">Strings::frame_for_response</span><span class="plain">(</span><span class="identifier">resp</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">phsf</span><span class="plain">) </span><span class="functiontext">LocalVariables::copy</span><span class="plain">(</span><span class="functiontext">Frames::current_stack_frame</span><span class="plain">(), </span><span class="identifier">phsf</span><span class="plain">);</span>
<span class="functiontext">LocalVariables::monitor_local_parsing</span><span class="plain">(</span><span class="functiontext">Frames::current_stack_frame</span><span class="plain">());</span>
&lt;<span class="cwebmacro">Compile a say-phrase</span> <span class="cwebmacronumber">11.1</span>&gt;<span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">makes_local_references</span><span class="plain"> =</span>
<span class="functiontext">LocalVariables::local_parsed_recently</span><span class="plain">(</span><span class="functiontext">Frames::current_stack_frame</span><span class="plain">());</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">makes_local_references</span><span class="plain">) {</span>
<span class="identifier">Produce::push_code_position</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">(), </span><span class="identifier">Produce::begin_position</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">()), </span><span class="identifier">Inter::Bookmarks::snapshot</span><span class="plain">(</span><span class="identifier">Packaging::at</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">())));</span>
<span class="functiontext">LocalVariables::compile_retrieval</span><span class="plain">(</span><span class="identifier">phsf</span><span class="plain">);</span>
<span class="identifier">Produce::pop_code_position</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="plain">}</span>
<span class="functiontext">Routines::end</span><span class="plain">(</span><span class="identifier">save</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_sb_needed</span><span class="plain">) {</span>
<span class="identifier">packaging_state</span><span class="plain"> </span><span class="identifier">save</span><span class="plain"> = </span><span class="functiontext">Emit::named_array_begin</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_iname</span><span class="plain">, </span><span class="identifier">K_value</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">makes_local_references</span><span class="plain">)</span>
<span class="functiontext">Emit::array_iname_entry</span><span class="plain">(</span><span class="functiontext">Hierarchy::find</span><span class="plain">(</span><span class="constant">CONSTANT_PERISHABLE_TEXT_STORAGE_HL</span><span class="plain">));</span>
<span class="reserved">else</span>
<span class="functiontext">Emit::array_iname_entry</span><span class="plain">(</span><span class="functiontext">Hierarchy::find</span><span class="plain">(</span><span class="constant">CONSTANT_PACKED_TEXT_STORAGE_HL</span><span class="plain">));</span>
<span class="functiontext">Emit::array_iname_entry</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;ts_routine_iname</span><span class="plain">);</span>
<span class="functiontext">Emit::array_end</span><span class="plain">(</span><span class="identifier">save</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">current_ts_being_compiled</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Strings::TextSubstitutions::compile_single_substitution is used in <a href="#SP10">&#167;10</a>.</p>
<p class="inwebparagraph"><a id="SP11_1"></a><b>&#167;11.1. </b>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...
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Compile a say-phrase</span> <span class="cwebmacronumber">11.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="constant">BEGIN_COMPILATION_MODE</span><span class="plain">;</span>
<span class="identifier">COMPILATION_MODE_EXIT</span><span class="plain">(</span><span class="constant">IMPLY_NEWLINES_IN_SAY_CMODE</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">this_is_a_release_compile</span><span class="plain"> == </span><span class="identifier">FALSE</span><span class="plain">) || (</span><span class="identifier">this_is_a_debug_compile</span><span class="plain">)) {</span>
<span class="identifier">Produce::inv_primitive</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">(), </span><span class="identifier">IFDEBUG_BIP</span><span class="plain">);</span>
<span class="identifier">Produce::down</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::code</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::down</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::inv_primitive</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">(), </span><span class="identifier">IF_BIP</span><span class="plain">);</span>
<span class="identifier">Produce::down</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::val_iname</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">(), </span><span class="identifier">K_number</span><span class="plain">, </span><span class="functiontext">Hierarchy::find</span><span class="plain">(</span><span class="constant">SUPPRESS_TEXT_SUBSTITUTION_HL</span><span class="plain">));</span>
<span class="identifier">Produce::code</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::down</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::inv_primitive</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">(), </span><span class="identifier">PRINT_BIP</span><span class="plain">);</span>
<span class="identifier">Produce::down</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="string">"%W"</span><span class="plain">, </span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;unsubstituted_text</span><span class="plain">);</span>
<span class="identifier">Produce::val_text</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">(), </span><span class="identifier">S</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="identifier">Produce::up</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::rtrue</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::up</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::up</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::up</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="identifier">Produce::up</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="plain">}</span>
<span class="identifier">parse_node</span><span class="plain"> *</span><span class="identifier">ts_code_block</span><span class="plain"> = </span><span class="identifier">ParseTree::new</span><span class="plain">(</span><span class="constant">ROUTINE_NT</span><span class="plain">);</span>
<span class="identifier">ParseTree::set_module</span><span class="plain">(</span><span class="identifier">ts_code_block</span><span class="plain">, </span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;belongs_to_module</span><span class="plain">);</span>
<span class="identifier">compilation_module</span><span class="plain"> *</span><span class="identifier">cm</span><span class="plain"> = </span><span class="functiontext">Modules::current</span><span class="plain">();</span>
<span class="functiontext">Modules::set_current_to</span><span class="plain">(</span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;belongs_to_module</span><span class="plain">);</span>
<span class="identifier">ts_code_block</span><span class="plain">-</span><span class="element">&gt;down</span><span class="plain"> = </span><span class="identifier">ParseTree::new</span><span class="plain">(</span><span class="constant">INVOCATION_LIST_NT</span><span class="plain">);</span>
<span class="identifier">ParseTree::set_text</span><span class="plain">(</span><span class="identifier">ts_code_block</span><span class="plain">-</span><span class="element">&gt;down</span><span class="plain">, </span><span class="identifier">ts</span><span class="plain">-</span><span class="element">&gt;unsubstituted_text</span><span class="plain">);</span>
<span class="identifier">ParseTree::annotate_int</span><span class="plain">(</span><span class="identifier">ts_code_block</span><span class="plain">-</span><span class="element">&gt;down</span><span class="plain">, </span><span class="constant">from_text_substitution_ANNOT</span><span class="plain">, </span><span class="identifier">TRUE</span><span class="plain">);</span>
<span class="functiontext">Sentences::RuleSubtrees::parse_routine_structure</span><span class="plain">(</span><span class="identifier">ts_code_block</span><span class="plain">);</span>
<span class="functiontext">Routines::Compile::code_block_outer</span><span class="plain">(0, </span><span class="identifier">ts_code_block</span><span class="plain">-</span><span class="element">&gt;down</span><span class="plain">);</span>
<span class="identifier">Produce::rtrue</span><span class="plain">(</span><span class="functiontext">Emit::tree</span><span class="plain">());</span>
<span class="constant">END_COMPILATION_MODE</span><span class="plain">;</span>
<span class="functiontext">Modules::set_current_to</span><span class="plain">(</span><span class="identifier">cm</span><span class="plain">);</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP11">&#167;11</a>.</p>
<p class="inwebparagraph"><a id="SP12"></a><b>&#167;12. </b>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 &mdash; which would be very inefficient except that in this mode
we aren't doing very much; most TSs will be passed quickly over.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Strings::TextSubstitutions::compile_text_routines_in_response_mode</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="identifier">latest_ts_compiled</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="functiontext">Strings::TextSubstitutions::compilation_coroutine</span><span class="plain">(</span><span class="identifier">TRUE</span><span class="plain">);</span>
<span class="identifier">latest_ts_compiled</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Strings::TextSubstitutions::compile_text_routines_in_response_mode is used in 17/rs (<a href="17-rs.html#SP7">&#167;7</a>).</p>
<hr class="tocbar">
<ul class="toc"><li><a href="17-tl.html">Back to 'Text Literals'</a></li><li><a href="17-rs.html">Continue with 'Responses'</a></li></ul><hr class="tocbar">
<!--End of weave-->
</body>
</html>