1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-05 16:44:21 +03:00
inform7/docs/inblorb/3-sd.html
2020-03-23 21:42:00 +00:00

658 lines
75 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>3/rls</title>
<meta name="viewport" content="width=device-width initial-scale=1">
<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>
<nav role="navigation">
<h1><a href="../webs.html">Sources</a></h1>
<ul>
<li><a href="../compiler.html">compiler</a></li>
<li><a href="../other.html"><b>other tools</b></a></li>
<li><a href="../extensions.html">extensions and kits</a></li>
<li><a href="../units.html">unit test tools</a></li>
</ul>
<h2>Other Tools</h2>
<ul>
<li><a href="../inblorb/index.html">inblorb</a></li>
<li><a href="../indoc/index.html">indoc</a></li>
<li><a href="../inpolicy/index.html">inpolicy</a></li>
<li><a href="../inrtps/index.html">inrtps</a></li>
</ul>
<h2>Foundation</h2>
<ul>
<li><a href="../../../inweb/docs/foundation-module/index.html">foundation</a></li>
</ul>
</nav>
<main role="main">
<!--Weave of '3/sd' generated by 7-->
<ul class="crumbs"><li><a href="../webs.html">Source</a></li><li><a href="../other.html">Other Tools</a></li><li><a href="index.html">inblorb</a></li><li><a href="index.html#3">Chapter 3: Other Material</a></li><li><b>Solution Deviser</b></li></ul><p class="purpose">To make a solution (.sol) file accompanying a release, if requested.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. Skein storage</a></li><li><a href="#SP3">&#167;3. Walking through</a></li><li><a href="#SP4">&#167;4. Step 1: building the Skein tree</a></li><li><a href="#SP10">&#167;10. Step 2: identify the relevant lines</a></li><li><a href="#SP11">&#167;11. Step 3: pruning irrelevant lines out of the tree</a></li><li><a href="#SP12">&#167;12. Step 4: writing the solution file</a></li><li><a href="#SP14">&#167;14. Writing individual commands and branch descriptions</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. Skein storage. </b>A solution file is simply a list of commands which will win a work of IF,
starting from turn 1. In this section we will generate this list given the
Skein file for an Inform 7 project: to follow this code, it's useful first
to read the "Walkthrough solutions" section of the "Releasing" chapter
in the main Inform documentation.
</p>
<p class="inwebparagraph">We will need to parse the entire skein into a tree structure, in which each
node (including leaves) is one of the following structures. We expect the
Inform user to have annotated certain nodes with the text <code class="display"><span class="extract">***</span></code> (three
asterisks); the solution file will ignore all paths in the skein which do
not lead to one of these <code class="display"><span class="extract">***</span></code> nodes. The surviving nodes, those in lines
which do lead to <code class="display"><span class="extract">***</span></code> endings, are called "relevant".
</p>
<p class="inwebparagraph">Some knots have "branch descriptions", others do not. These are the
options where choices have to be made. The <code class="display"><span class="extract">branch_parent</span></code> and <code class="display"><span class="extract">branch_count</span></code>
fields are used to keep these labels: see below.
</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">skein_node</span><span class="plain"> {</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">id</span><span class="plain">; </span> <span class="comment">uniquely identifying ID used within the Skein file</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">command</span><span class="plain">; </span> <span class="comment">text of the command at this node</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">annotation</span><span class="plain">; </span> <span class="comment">text of any annotation added by the user</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">relevant</span><span class="plain">; </span> <span class="comment">is this node within one of the "relevant" lines in the skein?</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">branch_parent</span><span class="plain">; </span> <span class="comment">the trunk of the branch description, if any, is this way</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">branch_count</span><span class="plain">; </span> <span class="comment">the leaf of the branch description, if any, is this number</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">parent</span><span class="plain">; </span> <span class="comment">within the Skein tree: <code class="display"><span class="extract">NULL</span></code> for the root only</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">child</span><span class="plain">; </span> <span class="comment">within the Skein tree: <code class="display"><span class="extract">NULL</span></code> if a leaf</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">sibling</span><span class="plain">; </span> <span class="comment">within the Skein tree: <code class="display"><span class="extract">NULL</span></code> if the final option from its parent</span>
<span class="constant">MEMORY_MANAGEMENT</span>
<span class="plain">} </span><span class="reserved">skein_node</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure skein_node is private to this section.</p>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. </b>The root of the Skein, representing the start position before any command
is typed, lives here:
</p>
<pre class="display">
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">root_skn</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">; </span> <span class="comment">only <code class="display"><span class="extract">NULL</span></code> when the tree is empty</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. Walking through. </b>This section provides just one function to the rest of Inblorb: this one,
which implements the Blurb <code class="display"><span class="extract">solution</span></code> command.
</p>
<p class="inwebparagraph">Our method works in four steps. Steps 1 to 3 have a running time of O(K^2),
where K is the number of knots in the Skein, and step 4 is O(K log K),
so the process as a whole is O(K^2).
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::walkthrough</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">Skein_filename</span><span class="plain">, </span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">walkthrough_filename</span><span class="plain">) {</span>
<span class="functiontext">Solution::build_skein_tree</span><span class="plain">(</span><span class="identifier">Skein_filename</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">root_skn</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="functiontext">BlorbErrors::error</span><span class="plain">(</span><span class="string">"there appear to be no threads in the Skein"</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="functiontext">Solution::identify_relevant_lines</span><span class="plain">();</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">root_skn</span><span class="plain">-</span><span class="element">&gt;relevant</span><span class="plain"> == </span><span class="constant">FALSE</span><span class="plain">) {</span>
<span class="functiontext">BlorbErrors::error</span><span class="plain">(</span><span class="string">"no threads in the Skein have been marked '***'"</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="functiontext">Solution::prune_irrelevant_lines</span><span class="plain">();</span>
<span class="functiontext">Solution::write_solution_file</span><span class="plain">(</span><span class="identifier">walkthrough_filename</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::walkthrough is used in 3/rls (<a href="3-rls.html#SP6">&#167;6</a>, <a href="3-rls.html#SP6_1">&#167;6.1</a>).</p>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. Step 1: building the Skein tree. </b></p>
<pre class="display">
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">current_skein_node</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">Solution::build_skein_tree</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">Skein_filename</span><span class="plain">) {</span>
<span class="identifier">root_skn</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">current_skein_node</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="functiontext">TextFiles::read</span><span class="plain">(</span><span class="identifier">Skein_filename</span><span class="plain">, </span><span class="constant">FALSE</span><span class="plain">, </span><span class="string">"can't open skein file"</span><span class="plain">, </span><span class="constant">FALSE</span><span class="plain">, </span><span class="functiontext">Solution::read_skein_pass_1</span><span class="plain">, 0, </span><span class="identifier">NULL</span><span class="plain">);</span>
<span class="identifier">current_skein_node</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="functiontext">TextFiles::read</span><span class="plain">(</span><span class="identifier">Skein_filename</span><span class="plain">, </span><span class="constant">FALSE</span><span class="plain">, </span><span class="string">"can't open skein file"</span><span class="plain">, </span><span class="constant">FALSE</span><span class="plain">, </span><span class="functiontext">Solution::read_skein_pass_2</span><span class="plain">, 0, </span><span class="identifier">NULL</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::read_skein_pass_1</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">line</span><span class="plain">, </span><span class="reserved">text_file_position</span><span class="plain"> *</span><span class="identifier">tfp</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">state</span><span class="plain">) { </span><span class="functiontext">Solution::read_skein_line</span><span class="plain">(</span><span class="identifier">line</span><span class="plain">, 1); }</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::read_skein_pass_2</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">line</span><span class="plain">, </span><span class="reserved">text_file_position</span><span class="plain"> *</span><span class="identifier">tfp</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">state</span><span class="plain">) { </span><span class="functiontext">Solution::read_skein_line</span><span class="plain">(</span><span class="identifier">line</span><span class="plain">, 2); }</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::build_skein_tree is used in <a href="#SP3">&#167;3</a>.</p>
<p class="endnote">The function Solution::read_skein_pass_1 appears nowhere else.</p>
<p class="endnote">The function Solution::read_skein_pass_2 appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. </b>The Skein is stored as an XML file. Its format was devised by Andrew Hunter
in the early days of the Inform user interface for Mac OS X, and this was
then adopted by the user interface on other platforms, so that projects could
be freely exchanged between users regardless of their platforms. That makes
it a kind of standard, but it isn't at present a public or documented one.
We shall therefore make few assumptions about it.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::read_skein_line</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">line</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">pass</span><span class="plain">) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">node_id</span><span class="plain">);</span>
<span class="functiontext">Solution::find_node_ID_in_tag</span><span class="plain">(</span><span class="identifier">node_id</span><span class="plain">, </span><span class="identifier">line</span><span class="plain">, </span><span class="string">"item"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">pass</span><span class="plain"> == 1) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">node_id</span><span class="plain">) &gt; 0) </span>&lt;<span class="cwebmacro">Create a new skein tree node with this node ID</span> <span class="cwebmacronumber">5.1</span>&gt;<span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">current_skein_node</span><span class="plain">) {</span>
&lt;<span class="cwebmacro">Look for a "command" tag and set the command text from it</span> <span class="cwebmacronumber">5.3</span>&gt;<span class="plain">;</span>
&lt;<span class="cwebmacro">Look for an "annotation" tag and set the annotation text from it</span> <span class="cwebmacronumber">5.4</span>&gt;<span class="plain">;</span>
<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="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">node_id</span><span class="plain">) &gt; 0) </span><span class="identifier">current_skein_node</span><span class="plain"> = </span><span class="functiontext">Solution::find_node_with_ID</span><span class="plain">(</span><span class="identifier">node_id</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">current_skein_node</span><span class="plain">) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">child_node_id</span><span class="plain">);</span>
<span class="functiontext">Solution::find_node_ID_in_tag</span><span class="plain">(</span><span class="identifier">child_node_id</span><span class="plain">, </span><span class="identifier">line</span><span class="plain">, </span><span class="string">"child"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">child_node_id</span><span class="plain">) &gt; 0) {</span>
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">new_child</span><span class="plain"> = </span><span class="functiontext">Solution::find_node_with_ID</span><span class="plain">(</span><span class="identifier">child_node_id</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">new_child</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="functiontext">BlorbErrors::error</span><span class="plain">(</span><span class="string">"the skein file is malformed (B)"</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain">;</span>
<span class="plain">}</span>
&lt;<span class="cwebmacro">Make the parent-child relationship</span> <span class="cwebmacronumber">5.2</span>&gt;<span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">child_node_id</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">node_id</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::read_skein_line is used in <a href="#SP4">&#167;4</a>.</p>
<p class="inwebparagraph"><a id="SP5_1"></a><b>&#167;5.1. </b>Note that the root is the first knot in the Skein file.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Create a new skein tree node with this node ID</span> <span class="cwebmacronumber">5.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="identifier">current_skein_node</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">skein_node</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">root_skn</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">root_skn</span><span class="plain"> = </span><span class="identifier">current_skein_node</span><span class="plain">;</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;id</span><span class="plain"> = </span><span class="functiontext">Str::duplicate</span><span class="plain">(</span><span class="identifier">node_id</span><span class="plain">);</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;command</span><span class="plain"> = </span><span class="functiontext">Str::new</span><span class="plain">();</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;annotation</span><span class="plain"> = </span><span class="functiontext">Str::new</span><span class="plain">();</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;branch_count</span><span class="plain"> = -1;</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;branch_parent</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;parent</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;relevant</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">verbose_mode</span><span class="plain">) </span><span class="identifier">PRINT</span><span class="plain">(</span><span class="string">"Creating knot with ID '%S'\</span><span class="plain">n</span><span class="string">"</span><span class="plain">, </span><span class="identifier">node_id</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="SP5_2"></a><b>&#167;5.2. </b>We make <code class="display"><span class="extract">new_child</span></code> the youngest child of <code class="display"><span class="extract">current_skein_mode</span></code>:
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Make the parent-child relationship</span> <span class="cwebmacronumber">5.2</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="identifier">new_child</span><span class="plain">-</span><span class="element">&gt;parent</span><span class="plain"> = </span><span class="identifier">current_skein_node</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain"> = </span><span class="identifier">new_child</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">familial</span><span class="plain"> = </span><span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">familial</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain">) </span><span class="identifier">familial</span><span class="plain"> = </span><span class="identifier">familial</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain">;</span>
<span class="identifier">familial</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain"> = </span><span class="identifier">new_child</span><span class="plain">;</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="SP5_3"></a><b>&#167;5.3. </b><code class="display">
&lt;<span class="cwebmacrodefn">Look for a "command" tag and set the command text from it</span> <span class="cwebmacronumber">5.3</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">p</span><span class="plain"> = </span><span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;command</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Solution::find_text_of_tag</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">line</span><span class="plain">, </span><span class="string">"command"</span><span class="plain">)) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">verbose_mode</span><span class="plain">) </span><span class="identifier">PRINT</span><span class="plain">(</span><span class="string">"Raw command '%S'\</span><span class="plain">n</span><span class="string">"</span><span class="plain">, </span><span class="identifier">p</span><span class="plain">);</span>
<span class="functiontext">Solution::undo_XML_escapes_in_string</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">);</span>
<span class="identifier">LOOP_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">, </span><span class="identifier">p</span><span class="plain">)</span>
<span class="functiontext">Str::put</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">, </span><span class="functiontext">Characters::toupper</span><span class="plain">(</span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">)));</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">verbose_mode</span><span class="plain">) </span><span class="identifier">PRINT</span><span class="plain">(</span><span class="string">"Processed command '%S'\</span><span class="plain">n</span><span class="string">"</span><span class="plain">, </span><span class="identifier">p</span><span class="plain">);</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="SP5_4"></a><b>&#167;5.4. </b><code class="display">
&lt;<span class="cwebmacrodefn">Look for an "annotation" tag and set the annotation text from it</span> <span class="cwebmacronumber">5.4</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">p</span><span class="plain"> = </span><span class="identifier">current_skein_node</span><span class="plain">-</span><span class="element">&gt;annotation</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Solution::find_text_of_tag</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">line</span><span class="plain">, </span><span class="string">"annotation"</span><span class="plain">)) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">verbose_mode</span><span class="plain">) </span><span class="identifier">PRINT</span><span class="plain">(</span><span class="string">"Raw annotation '%S'\</span><span class="plain">n</span><span class="string">"</span><span class="plain">, </span><span class="identifier">p</span><span class="plain">);</span>
<span class="functiontext">Solution::undo_XML_escapes_in_string</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">verbose_mode</span><span class="plain">) </span><span class="identifier">PRINT</span><span class="plain">(</span><span class="string">"Processed annotation '%S'\</span><span class="plain">n</span><span class="string">"</span><span class="plain">, </span><span class="identifier">p</span><span class="plain">);</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>Try to find a node ID element attached to a particular tag on the line:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::find_node_ID_in_tag</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">line</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">tag</span><span class="plain">) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">prototype</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">prototype</span><span class="plain">, </span><span class="string">"%c*?&lt;%s nodeId=\</span><span class="plain">"</span><span class="string">(%c*?)\</span><span class="plain">"</span><span class="string">%c*"</span><span class="plain">, </span><span class="identifier">tag</span><span class="plain">);</span>
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">prototype_Cs</span><span class="plain">[128];</span>
<span class="functiontext">Str::copy_to_wide_string</span><span class="plain">(</span><span class="identifier">prototype_Cs</span><span class="plain">, </span><span class="identifier">prototype</span><span class="plain">, 128);</span>
<span class="reserved">match_results</span><span class="plain"> </span><span class="identifier">mr</span><span class="plain"> = </span><span class="functiontext">Regexp::create_mr</span><span class="plain">();</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Regexp::match</span><span class="plain">(&amp;</span><span class="identifier">mr</span><span class="plain">, </span><span class="identifier">line</span><span class="plain">, </span><span class="identifier">prototype_Cs</span><span class="plain">)) </span><span class="functiontext">Str::copy</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">mr</span><span class="element">.exp</span><span class="plain">[0]);</span>
<span class="reserved">else</span><span class="plain"> </span><span class="functiontext">Str::clear</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">);</span>
<span class="functiontext">Regexp::dispose_of</span><span class="plain">(&amp;</span><span class="identifier">mr</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">prototype</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::find_node_ID_in_tag is used in <a href="#SP5">&#167;5</a>.</p>
<p class="inwebparagraph"><a id="SP7"></a><b>&#167;7. </b>Try to find the text of a particular tag on the line:
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Solution::find_text_of_tag</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">line</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">tag</span><span class="plain">) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">prototype</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">prototype</span><span class="plain">, </span><span class="string">"%c*?&gt;(%c*?)&lt;/%s%c*"</span><span class="plain">, </span><span class="identifier">tag</span><span class="plain">);</span>
<span class="reserved">match_results</span><span class="plain"> </span><span class="identifier">mr</span><span class="plain"> = </span><span class="functiontext">Regexp::create_mr</span><span class="plain">();</span>
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">prototype_Cs</span><span class="plain">[128];</span>
<span class="functiontext">Str::copy_to_wide_string</span><span class="plain">(</span><span class="identifier">prototype_Cs</span><span class="plain">, </span><span class="identifier">prototype</span><span class="plain">, 128);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Regexp::match</span><span class="plain">(&amp;</span><span class="identifier">mr</span><span class="plain">, </span><span class="identifier">line</span><span class="plain">, </span><span class="identifier">prototype_Cs</span><span class="plain">)) {</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">prototype</span><span class="plain">);</span>
<span class="functiontext">Str::copy</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">mr</span><span class="element">.exp</span><span class="plain">[0]);</span>
<span class="functiontext">Regexp::dispose_of</span><span class="plain">(&amp;</span><span class="identifier">mr</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">prototype</span><span class="plain">);</span>
<span class="functiontext">Str::clear</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::find_text_of_tag is used in <a href="#SP5_3">&#167;5.3</a>, <a href="#SP5_4">&#167;5.4</a>.</p>
<p class="inwebparagraph"><a id="SP8"></a><b>&#167;8. </b>This is not very efficient, but:
</p>
<pre class="display">
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="functiontext">Solution::find_node_with_ID</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">id</span><span class="plain">) {</span>
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">skn</span><span class="plain">;</span>
<span class="identifier">LOOP_OVER</span><span class="plain">(</span><span class="identifier">skn</span><span class="plain">, </span><span class="reserved">skein_node</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq</span><span class="plain">(</span><span class="identifier">id</span><span class="plain">, </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;id</span><span class="plain">) == 0)</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">skn</span><span class="plain">;</span>
<span class="reserved">return</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 Solution::find_node_with_ID is used in <a href="#SP5">&#167;5</a>.</p>
<p class="inwebparagraph"><a id="SP9"></a><b>&#167;9. </b>Finally, we needed the following string hackery:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::undo_XML_escapes_in_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">p</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain"> = 0, </span><span class="identifier">j</span><span class="plain"> = 0;</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">)) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">) == </span><span class="character">'&amp;'</span><span class="plain">) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">k</span><span class="plain">=0;</span>
<span class="reserved">while</span><span class="plain"> ((</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">+</span><span class="identifier">k</span><span class="plain">) != 0) &amp;&amp; (</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">+</span><span class="identifier">k</span><span class="plain">) != </span><span class="character">';'</span><span class="plain">))</span>
<span class="identifier">PUT_TO</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">, </span><span class="functiontext">Characters::tolower</span><span class="plain">(</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain"> + </span><span class="identifier">k</span><span class="plain">++)));</span>
<span class="identifier">PUT_TO</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">, </span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">+</span><span class="identifier">k</span><span class="plain">));</span>
&lt;<span class="cwebmacro">We have identified an XML escape</span> <span class="cwebmacronumber">9.1</span>&gt;<span class="plain">;</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="functiontext">Str::put_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">++, </span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">++));</span>
<span class="plain">}</span>
<span class="functiontext">Str::put_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">++, 0);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::undo_XML_escapes_in_string is used in <a href="#SP5_3">&#167;5.3</a>, <a href="#SP5_4">&#167;5.4</a>.</p>
<p class="inwebparagraph"><a id="SP9_1"></a><b>&#167;9.1. </b>Note that all other ampersand-escapes are passed through verbatim.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">We have identified an XML escape</span> <span class="cwebmacronumber">9.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">c</span><span class="plain"> = 0;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">, </span><span class="identifier">L</span><span class="string">"&amp;lt;"</span><span class="plain">)) </span><span class="identifier">c</span><span class="plain"> = </span><span class="character">'&lt;'</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">, </span><span class="identifier">L</span><span class="string">"&amp;gt;"</span><span class="plain">)) </span><span class="identifier">c</span><span class="plain"> = </span><span class="character">'&gt;'</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">, </span><span class="identifier">L</span><span class="string">"&amp;amp;"</span><span class="plain">)) </span><span class="identifier">c</span><span class="plain"> = </span><span class="character">'&amp;'</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">, </span><span class="identifier">L</span><span class="string">"&amp;apos;"</span><span class="plain">)) </span><span class="identifier">c</span><span class="plain"> = </span><span class="character">'\</span><span class="plain">'</span><span class="character">'</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">, </span><span class="identifier">L</span><span class="string">"&amp;quot;"</span><span class="plain">)) </span><span class="identifier">c</span><span class="plain"> = </span><span class="character">'\</span><span class="plain">"</span><span class="character">'</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain">) { </span><span class="functiontext">Str::put_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">++, </span><span class="identifier">c</span><span class="plain">); </span><span class="identifier">i</span><span class="plain"> += </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">xml_escape</span><span class="plain">); </span><span class="reserved">continue</span><span class="plain">; }</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP9">&#167;9</a>.</p>
<p class="inwebparagraph"><a id="SP10"></a><b>&#167;10. Step 2: identify the relevant lines. </b>We aim to show how to reach all knots in the Skein annotated with text which
begins with three asterisks. (We trim those asterisks away from the annotation
once we spot them: they have served their purpose.) A knot is "relevant"
if and only if one of its (direct or indirect) children is marked with three
asterisks in this way.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::identify_relevant_lines</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">skn</span><span class="plain">;</span>
<span class="identifier">LOOP_OVER</span><span class="plain">(</span><span class="identifier">skn</span><span class="plain">, </span><span class="reserved">skein_node</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">p</span><span class="plain"> = </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;annotation</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">verbose_mode</span><span class="plain">) </span><span class="identifier">PRINT</span><span class="plain">(</span><span class="string">"Knot %S is annotated '%S'\</span><span class="plain">n</span><span class="string">"</span><span class="plain">, </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;id</span><span class="plain">, </span><span class="identifier">p</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, 0) == </span><span class="character">'*'</span><span class="plain">) &amp;&amp; (</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, 1) == </span><span class="character">'*'</span><span class="plain">) &amp;&amp; (</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, 2) == </span><span class="character">'*'</span><span class="plain">)) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain"> = 3, </span><span class="identifier">j</span><span class="plain">; </span><span class="reserved">while</span><span class="plain"> (</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">) == </span><span class="character">' '</span><span class="plain">) </span><span class="identifier">i</span><span class="plain">++;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">j</span><span class="plain">=0; </span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">); </span><span class="identifier">i</span><span class="plain">++) </span><span class="functiontext">Str::put_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">++, </span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">)); </span><span class="functiontext">Str::put_at</span><span class="plain">(</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">, 0);</span>
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">knot</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">knot</span><span class="plain"> = </span><span class="identifier">skn</span><span class="plain">; </span><span class="identifier">knot</span><span class="plain">; </span><span class="identifier">knot</span><span class="plain"> = </span><span class="identifier">knot</span><span class="plain">-</span><span class="element">&gt;parent</span><span class="plain">) {</span>
<span class="identifier">knot</span><span class="plain">-</span><span class="element">&gt;relevant</span><span class="plain"> = </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">verbose_mode</span><span class="plain">) </span><span class="identifier">PRINT</span><span class="plain">(</span><span class="string">"Knot %S is relevant\</span><span class="plain">n</span><span class="string">"</span><span class="plain">, </span><span class="identifier">knot</span><span class="plain">-</span><span class="element">&gt;id</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 Solution::identify_relevant_lines is used in <a href="#SP3">&#167;3</a>.</p>
<p class="inwebparagraph"><a id="SP11"></a><b>&#167;11. Step 3: pruning irrelevant lines out of the tree. </b>When the loop below concludes, the relevant nodes are exactly those in the
component of the tree root, because:
</p>
<p class="inwebparagraph"></p>
<ul class="items"><li>(a) No irrelevant node can be the child of a relevant one; and no
relevant node can be the child of an irrelevant one by definition. So the
tree falls into components each of which is fully relevant or fully not.
</li><li>(b) Since we never break any relevant-parent-relevant-child relationships, the
number of components containing at least one relevant node is unchanged.
</li><li>(c) Since the Skein is initially a tree and not a forest, we start with
just one component, and it contains the tree root, which is known to be
relevant (we would have given up with an error message if not).
</li><li>(d) And therefore at the end of the loop the "tree" consists of a single
component headed by the tree root and containing all of the relevant nodes,
together with any number of other components each of which contains only
irrelevant ones.
</li></ul>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::prune_irrelevant_lines</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">skn</span><span class="plain">;</span>
<span class="identifier">LOOP_OVER</span><span class="plain">(</span><span class="identifier">skn</span><span class="plain">, </span><span class="reserved">skein_node</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;relevant</span><span class="plain"> == </span><span class="constant">FALSE</span><span class="plain">) &amp;&amp; (</span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;parent</span><span class="plain">))</span>
&lt;<span class="cwebmacro">Delete this node from its parent</span> <span class="cwebmacronumber">11.1</span>&gt;<span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::prune_irrelevant_lines is used in <a href="#SP3">&#167;3</a>.</p>
<p class="inwebparagraph"><a id="SP11_1"></a><b>&#167;11.1. </b><code class="display">
&lt;<span class="cwebmacrodefn">Delete this node from its parent</span> <span class="cwebmacronumber">11.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;parent</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain"> == </span><span class="identifier">skn</span><span class="plain">) {</span>
<span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;parent</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain"> = </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">skn2</span><span class="plain"> = </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;parent</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> ((</span><span class="identifier">skn2</span><span class="plain">) &amp;&amp; (</span><span class="identifier">skn2</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain"> != </span><span class="identifier">skn</span><span class="plain">)) </span><span class="identifier">skn2</span><span class="plain"> = </span><span class="identifier">skn2</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">skn2</span><span class="plain">) &amp;&amp; (</span><span class="identifier">skn2</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain"> == </span><span class="identifier">skn</span><span class="plain">)) </span><span class="identifier">skn2</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain"> = </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;parent</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain"> = </span><span class="identifier">NULL</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. Step 4: writing the solution file. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::write_solution_file</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">walkthrough_filename</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> </span><span class="identifier">TO_struct</span><span class="plain">;</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">SOL</span><span class="plain"> = &amp;</span><span class="identifier">TO_struct</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">STREAM_OPEN_TO_FILE</span><span class="plain">(</span><span class="identifier">SOL</span><span class="plain">, </span><span class="identifier">walkthrough_filename</span><span class="plain">, </span><span class="constant">UTF8_ENC</span><span class="plain">) == </span><span class="constant">FALSE</span><span class="plain">)</span>
<span class="functiontext">BlorbErrors::fatal_fs</span><span class="plain">(</span><span class="string">"can't open solution text file for output"</span><span class="plain">, </span><span class="identifier">walkthrough_filename</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">SOL</span><span class="plain">, </span><span class="string">"Solution to \</span><span class="plain">"</span><span class="string">"</span><span class="plain">); </span><span class="functiontext">Placeholders::write</span><span class="plain">(</span><span class="identifier">SOL</span><span class="plain">, </span><span class="identifier">I</span><span class="string">"TITLE"</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">SOL</span><span class="plain">, </span><span class="string">"\</span><span class="plain">"</span><span class="string"> by "</span><span class="plain">); </span><span class="functiontext">Placeholders::write</span><span class="plain">(</span><span class="identifier">SOL</span><span class="plain">, </span><span class="identifier">I</span><span class="string">"AUTHOR"</span><span class="plain">); </span><span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">SOL</span><span class="plain">, </span><span class="string">"\</span><span class="plain">n</span><span class="string">\</span><span class="plain">n</span><span class="string">"</span><span class="plain">);</span>
<span class="functiontext">Solution::recursively_solve</span><span class="plain">(</span><span class="identifier">SOL</span><span class="plain">, </span><span class="identifier">root_skn</span><span class="plain">, </span><span class="identifier">NULL</span><span class="plain">);</span>
<span class="identifier">STREAM_CLOSE</span><span class="plain">(</span><span class="identifier">SOL</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::write_solution_file is used in <a href="#SP3">&#167;3</a>.</p>
<p class="inwebparagraph"><a id="SP13"></a><b>&#167;13. </b>The following prints commands to the solution file from the position <code class="display"><span class="extract">skn</span></code> &mdash;
which means just after typing its command &mdash; with the aim of reaching all
relevant endings we can get to from there.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::recursively_solve</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">skn</span><span class="plain">, </span><span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">last_branch</span><span class="plain">) {</span>
&lt;<span class="cwebmacro">Follow the skein down until we reach a divergence, if we do</span> <span class="cwebmacronumber">13.1</span>&gt;<span class="plain">;</span>
&lt;<span class="cwebmacro">Print the various alternatives from this knot where the threads diverge</span> <span class="cwebmacronumber">13.2</span>&gt;<span class="plain">;</span>
&lt;<span class="cwebmacro">Show the solutions down each of these alternative lines in turn</span> <span class="cwebmacronumber">13.3</span>&gt;<span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::recursively_solve is used in <a href="#SP12">&#167;12</a>, <a href="#SP13_3">&#167;13.3</a>.</p>
<p class="inwebparagraph"><a id="SP13_1"></a><b>&#167;13.1. </b>If there's only a single option from here, we could print it and then
call <code class="display"><span class="extract">Solution::recursively_solve</span></code> down from it. That would make the code shorter and
clearer, perhaps, but it would clobber the C stack: our recursion depth
might be into the tens of thousands on long solution files. So we tail-recurse
instead of calling ourselves, so to speak, and just run down the thread
until we reach a choice. (If we never do reach a choice, we can return &mdash;
there is nowhere else to reach.)
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Follow the skein down until we reach a divergence, if we do</span> <span class="cwebmacronumber">13.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">while</span><span class="plain"> ((</span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) || (</span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain">-</span><span class="element">&gt;sibling</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">skn</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain"> == </span><span class="identifier">NULL</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">skn</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="identifier">skn</span><span class="plain"> = </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain">;</span>
<span class="functiontext">Solution::write_command</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">skn</span><span class="plain">, </span><span class="constant">NORMAL_COMMAND</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="#SP13">&#167;13</a>.</p>
<p class="inwebparagraph"><a id="SP13_2"></a><b>&#167;13.2. </b>Thus we are here only when there are at least two alternative commands
we might use from position <code class="display"><span class="extract">skn</span></code>.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Print the various alternatives from this knot where the threads diverge</span> <span class="cwebmacronumber">13.2</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"Choice:\</span><span class="plain">n</span><span class="string">"</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">branch_counter</span><span class="plain"> = 1;</span>
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">option</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">option</span><span class="plain"> = </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain">; </span><span class="identifier">option</span><span class="plain">; </span><span class="identifier">option</span><span class="plain"> = </span><span class="identifier">option</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">option</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="functiontext">Solution::write_command</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">option</span><span class="plain">, </span><span class="constant">BRANCH_TO_END_COMMAND</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">option</span><span class="plain">-</span><span class="element">&gt;branch_count</span><span class="plain"> = </span><span class="identifier">branch_counter</span><span class="plain">++;</span>
<span class="identifier">option</span><span class="plain">-</span><span class="element">&gt;branch_parent</span><span class="plain"> = </span><span class="identifier">last_branch</span><span class="plain">;</span>
<span class="functiontext">Solution::write_command</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">option</span><span class="plain">, </span><span class="constant">BRANCH_TO_LINE_COMMAND</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP13">&#167;13</a>.</p>
<p class="inwebparagraph"><a id="SP13_3"></a><b>&#167;13.3. </b><code class="display">
&lt;<span class="cwebmacrodefn">Show the solutions down each of these alternative lines in turn</span> <span class="cwebmacronumber">13.3</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">option</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">option</span><span class="plain"> = </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain">; </span><span class="identifier">option</span><span class="plain">; </span><span class="identifier">option</span><span class="plain"> = </span><span class="identifier">option</span><span class="plain">-</span><span class="element">&gt;sibling</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">option</span><span class="plain">-</span><span class="element">&gt;child</span><span class="plain">) {</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"\</span><span class="plain">n</span><span class="string">Branch ("</span><span class="plain">);</span>
<span class="functiontext">Solution::write_branch_name</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">option</span><span class="plain">);</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">")\</span><span class="plain">n</span><span class="string">"</span><span class="plain">);</span>
<span class="functiontext">Solution::recursively_solve</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">option</span><span class="plain">, </span><span class="identifier">option</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP13">&#167;13</a>.</p>
<p class="inwebparagraph"><a id="SP14"></a><b>&#167;14. Writing individual commands and branch descriptions. </b></p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">NORMAL_COMMAND</span><span class="plain"> 1</span>
<span class="definitionkeyword">define</span> <span class="constant">BRANCH_TO_END_COMMAND</span><span class="plain"> 2</span>
<span class="definitionkeyword">define</span> <span class="constant">BRANCH_TO_LINE_COMMAND</span><span class="plain"> 3</span>
</pre>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::write_command</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">cmd_skn</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">form</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">form</span><span class="plain"> != </span><span class="constant">NORMAL_COMMAND</span><span class="plain">) </span><span class="identifier">WRITE</span><span class="plain">(</span><span class="string">" "</span><span class="plain">);</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"%S"</span><span class="plain">, </span><span class="identifier">cmd_skn</span><span class="plain">-</span><span class="element">&gt;command</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">form</span><span class="plain"> != </span><span class="constant">NORMAL_COMMAND</span><span class="plain">) {</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">" -&gt; "</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">form</span><span class="plain"> == </span><span class="constant">BRANCH_TO_LINE_COMMAND</span><span class="plain">) {</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"go to branch ("</span><span class="plain">);</span>
<span class="functiontext">Solution::write_branch_name</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">cmd_skn</span><span class="plain">);</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">")"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">else</span><span class="plain"> </span><span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"end"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">cmd_skn</span><span class="plain">-</span><span class="element">&gt;annotation</span><span class="plain">) &gt; 0) </span><span class="identifier">WRITE</span><span class="plain">(</span><span class="string">" ... %S"</span><span class="plain">, </span><span class="identifier">cmd_skn</span><span class="plain">-</span><span class="element">&gt;annotation</span><span class="plain">);</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"\</span><span class="plain">n</span><span class="string">"</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::write_command is used in <a href="#SP13_1">&#167;13.1</a>, <a href="#SP13_2">&#167;13.2</a>.</p>
<p class="inwebparagraph"><a id="SP15"></a><b>&#167;15. </b>For instance, at the third option from a thread which ran back to being
the second option from a thread which ran back to being the seventh option
from the original position, the following would print "7.2.3". Note that
only the knots representing the positions after commands which make a choice
are labelled in this way.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Solution::write_branch_name</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">skein_node</span><span class="plain"> *</span><span class="identifier">skn</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;branch_parent</span><span class="plain">) {</span>
<span class="functiontext">Solution::write_branch_name</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;branch_parent</span><span class="plain">);</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"."</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"%d"</span><span class="plain">, </span><span class="identifier">skn</span><span class="plain">-</span><span class="element">&gt;branch_count</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Solution::write_branch_name is used in <a href="#SP13_3">&#167;13.3</a>, <a href="#SP14">&#167;14</a>.</p>
<hr class="tocbar">
<ul class="toc"><li><a href="3-rls.html">Back to 'Releaser'</a></li><li><a href="3-laaf.html">Continue with 'Links and Auxiliary Files'</a></li></ul><hr class="tocbar">
<!--End of weave-->
</main>
</body>
</html>