mirror of
https://github.com/ganelson/inform.git
synced 2024-07-08 10:04:21 +03:00
448 lines
71 KiB
HTML
448 lines
71 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
|
<html>
|
|
<head>
|
|
<title>Architecture of the S-Parser</title>
|
|
<link href="../docs-assets/Breadcrumbs.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
<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="../docs-assets/Contents.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
<link href="../docs-assets/Progress.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
<link href="../docs-assets/Navigation.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
<link href="../docs-assets/Fonts.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
<link href="../docs-assets/Base.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
<script>
|
|
function togglePopup(material_id) {
|
|
var popup = document.getElementById(material_id);
|
|
popup.classList.toggle("show");
|
|
}
|
|
</script>
|
|
|
|
<link href="../docs-assets/Popups.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
<link href="../docs-assets/Colours.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
<link href="../docs-assets/Preform-Colours.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
|
|
</head>
|
|
<body class="commentary-font">
|
|
<nav role="navigation">
|
|
<h1><a href="../index.html">
|
|
<img src="../docs-assets/Inform.png" height=72">
|
|
</a></h1>
|
|
<ul><li><a href="../compiler.html">compiler tools</a></li>
|
|
<li><a href="../other.html">other tools</a></li>
|
|
<li><a href="../extensions.html">extensions and kits</a></li>
|
|
<li><a href="../units.html">unit test tools</a></li>
|
|
</ul><h2>Compiler Webs</h2><ul>
|
|
<li><a href="../inbuild/index.html">inbuild</a></li>
|
|
<li><a href="../inform7/index.html">inform7</a></li>
|
|
<li><a href="../inter/index.html">inter</a></li>
|
|
</ul><h2>Inbuild Modules</h2><ul>
|
|
<li><a href="../supervisor-module/index.html">supervisor</a></li>
|
|
</ul><h2>Inform7 Modules</h2><ul>
|
|
<li><a href="index.html"><span class="selectedlink">core</span></a></li>
|
|
<li><a href="../if-module/index.html">if</a></li>
|
|
<li><a href="../multimedia-module/index.html">multimedia</a></li>
|
|
<li><a href="../index-module/index.html">index</a></li>
|
|
</ul><h2>Inter Modules</h2><ul>
|
|
<li><a href="../bytecode-module/index.html">bytecode</a></li>
|
|
<li><a href="../building-module/index.html">building</a></li>
|
|
<li><a href="../codegen-module/index.html">codegen</a></li>
|
|
</ul><h2>Services</h2><ul>
|
|
<li><a href="../arch-module/index.html">arch</a></li>
|
|
<li><a href="../calculus-module/index.html">calculus</a></li>
|
|
<li><a href="../html-module/index.html">html</a></li>
|
|
<li><a href="../inflections-module/index.html">inflections</a></li>
|
|
<li><a href="../kinds-module/index.html">kinds</a></li>
|
|
<li><a href="../linguistics-module/index.html">linguistics</a></li>
|
|
<li><a href="../problems-module/index.html">problems</a></li>
|
|
<li><a href="../syntax-module/index.html">syntax</a></li>
|
|
<li><a href="../words-module/index.html">words</a></li>
|
|
<li><a href="../../../inweb/docs/foundation-module/index.html">foundation</a></li>
|
|
|
|
</ul>
|
|
</nav>
|
|
<main role="main">
|
|
<!--Weave of 'Architecture of the S-Parser' generated by Inweb-->
|
|
<div class="breadcrumbs">
|
|
<ul class="crumbs"><li><a href="../index.html">Home</a></li><li><a href="../compiler.html">Inform7 Modules</a></li><li><a href="index.html">core</a></li><li><a href="index.html#10">Chapter 10: The S-Parser</a></li><li><b>Architecture of the S-Parser</b></li></ul></div>
|
|
<p class="purpose">Top-level structure of the S-parser, which turns text into S-nodes.</p>
|
|
|
|
<ul class="toc"><li><a href="10-aots.html#SP1">§1. Introduction</a></li><li><a href="10-aots.html#SP3">§3. Top-level nonterminals</a></li><li><a href="10-aots.html#SP11">§11. The cache</a></li><li><a href="10-aots.html#SP15">§15. Void phrases</a></li></ul><hr class="tocbar">
|
|
|
|
<p class="commentary firstcommentary"><a id="SP1" class="paragraph-anchor"></a><b>§1. Introduction. </b>The purpose of the S-parser is to turn excerpts of text into specification
|
|
nodes. Nonterminals here almost all have names beginning with the "s-" prefix,
|
|
which indicates that their results are S-nodes.
|
|
</p>
|
|
|
|
<p class="commentary">The simplest nonterminal in the S-grammar is <s-plain-text>, which
|
|
accepts any non-empty piece of text. (The same can be said exactly of
|
|
<np-unparsed>, and the difference is purely to do with how Inform stores
|
|
the results: <np-unparsed> makes nodes in the main parse tree, a rather
|
|
permanent structure, whereas <s-plain-text> makes an S-node.)
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-plain-text></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><a href="14-sp.html#SP10" class="function-link"><span class="Preform-function-syntax">Specifications::new_UNKNOWN</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">) };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP2" class="paragraph-anchor"></a><b>§2. </b>And here is a curious variation, which is needed because equations are
|
|
parsed with completely different spacing rules, and don't respect words. It
|
|
matches any non-empty text where one of the words contains an equals sign
|
|
as one of its characters: thus
|
|
</p>
|
|
|
|
<blockquote>
|
|
<p>V = fl</p>
|
|
</blockquote>
|
|
|
|
<blockquote>
|
|
<p>F=ma</p>
|
|
</blockquote>
|
|
|
|
<p class="commentary">both match this, the first example being three words long, the second only one.
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-plain-text-with-equals></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">LOOP_THROUGH_WORDING</span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">i</span><span class="Preform-plain-syntax">, </span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">) {</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">wchar_t</span><span class="Preform-plain-syntax"> *</span><span class="Preform-identifier-syntax">p</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">Lexer::word_raw_text</span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">i</span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">for</span><span class="Preform-plain-syntax"> (</span><span class="Preform-reserved-syntax">int</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">j</span><span class="Preform-plain-syntax">=0; </span><span class="Preform-identifier-syntax">p</span><span class="Preform-plain-syntax">[</span><span class="Preform-identifier-syntax">j</span><span class="Preform-plain-syntax">]; </span><span class="Preform-identifier-syntax">j</span><span class="Preform-plain-syntax">++)</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">p</span><span class="Preform-plain-syntax">[</span><span class="Preform-identifier-syntax">j</span><span class="Preform-plain-syntax">] == </span><span class="Preform-character-syntax">'='</span><span class="Preform-plain-syntax">) {</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><a href="14-sp.html#SP10" class="function-link"><span class="Preform-function-syntax">Specifications::new_UNKNOWN</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">) };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> }</span>
|
|
<span class="Preform-plain-syntax"> }</span>
|
|
<span class="Preform-plain-syntax"> ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP3" class="paragraph-anchor"></a><b>§3. Top-level nonterminals. </b>These five nonterminals are the most useful and powerful, so they're the
|
|
main junction between the S-parser and the rest of Inform.
|
|
</p>
|
|
|
|
<p class="commentary">These are coded as internals for efficiency's sake. We will often reparse the
|
|
same wording over and over, so we cache the results. But <s-value> matches
|
|
exactly the same text as <s-value-uncached>, and so on for the other four.
|
|
</p>
|
|
|
|
<p class="commentary"><s-value> looks for source text which can be evaluated — a constant, a
|
|
variable or other storage object, or a phrase to decide a value.
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-value></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">parse_node</span><span class="Preform-plain-syntax"> *</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> = </span><a href="10-aots.html#SP13" class="function-link"><span class="Preform-function-syntax">ExParser::parse_with_cache</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">, </span><span class="Preform-constant-syntax">0</span><span class="Preform-plain-syntax">, </span><span class="Preform-function-syntax"><s-value-uncached></span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">Node::is</span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax">, </span><span class="Preform-identifier-syntax">UNKNOWN_NT</span><span class="Preform-plain-syntax">)) { ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> }; }</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP4" class="paragraph-anchor"></a><b>§4. </b><s-condition> looks for a condition — anything legal after an
|
|
"if", in short. This includes sentence-like excerpts such as "six
|
|
animals have been in the Stables".
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-condition></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> </span><a href="24-lv.html#SP31" class="function-link"><span class="Preform-function-syntax">LocalVariables::make_necessary_callings</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">parse_node</span><span class="Preform-plain-syntax"> *</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> = </span><a href="10-aots.html#SP13" class="function-link"><span class="Preform-function-syntax">ExParser::parse_with_cache</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">, </span><span class="Preform-constant-syntax">1</span><span class="Preform-plain-syntax">, </span><span class="Preform-function-syntax"><s-condition-uncached></span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">Node::is</span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax">, </span><span class="Preform-identifier-syntax">UNKNOWN_NT</span><span class="Preform-plain-syntax">)) { ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> }; }</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP5" class="paragraph-anchor"></a><b>§5. </b><s-non-action-condition> is the same, but disallowing action patterns
|
|
as conditions, so for example "taking something" would not match.
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-non-action-condition></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> </span><a href="24-lv.html#SP31" class="function-link"><span class="Preform-function-syntax">LocalVariables::make_necessary_callings</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> #</span><span class="Preform-identifier-syntax">ifdef</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">IF_MODULE</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">int</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">old_state</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">PL::Actions::Patterns::suppress</span><span class="Preform-plain-syntax">();</span>
|
|
<span class="Preform-plain-syntax"> #</span><span class="Preform-identifier-syntax">endif</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">parse_node</span><span class="Preform-plain-syntax"> *</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> = </span><a href="10-aots.html#SP13" class="function-link"><span class="Preform-function-syntax">ExParser::parse_with_cache</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">, </span><span class="Preform-constant-syntax">2</span><span class="Preform-plain-syntax">, </span><span class="Preform-function-syntax"><s-condition-uncached></span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> #</span><span class="Preform-identifier-syntax">ifdef</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">IF_MODULE</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">PL::Actions::Patterns::resume</span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">old_state</span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> #</span><span class="Preform-identifier-syntax">endif</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">Node::is</span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax">, </span><span class="Preform-identifier-syntax">UNKNOWN_NT</span><span class="Preform-plain-syntax">)) { ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> }; }</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP6" class="paragraph-anchor"></a><b>§6. </b><s-type-expression> is for where we expect to find the "type" of something
|
|
— for instance, the kind of value to be stored in a variable, or the
|
|
specification of a phrase argument.
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-type-expression></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">parse_node</span><span class="Preform-plain-syntax"> *</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> = </span><a href="10-aots.html#SP13" class="function-link"><span class="Preform-function-syntax">ExParser::parse_with_cache</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">, </span><span class="Preform-constant-syntax">3</span><span class="Preform-plain-syntax">, </span><span class="Preform-function-syntax"><s-type-expression-uncached></span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">Node::is</span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax">, </span><span class="Preform-identifier-syntax">UNKNOWN_NT</span><span class="Preform-plain-syntax">)) { ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> }; }</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP7" class="paragraph-anchor"></a><b>§7. </b><s-descriptive-type-expression> is the same thing, with one difference: it
|
|
allows nounless descriptions, such as "open opaque fixed in place", and to
|
|
this end it treats bare adjective names as descriptions rather than values. If
|
|
we have said "Colour is a kind of value. The colours are red, green and taupe.
|
|
A thing has a colour.", then "green" is parsed by <s-descriptive-type-expression>
|
|
as a description meaning "any thing which is green", but by <s-type-expression>
|
|
and <s-value> as a constant value of the kind "colour".
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-descriptive-type-expression></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">parse_node</span><span class="Preform-plain-syntax"> *</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> = </span><a href="10-aots.html#SP13" class="function-link"><span class="Preform-function-syntax">ExParser::parse_with_cache</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">, </span><span class="Preform-constant-syntax">4</span><span class="Preform-plain-syntax">, </span><span class="Preform-function-syntax"><s-descriptive-type-expression-uncached></span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">Node::is</span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax">, </span><span class="Preform-identifier-syntax">UNKNOWN_NT</span><span class="Preform-plain-syntax">)) { ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> }; }</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">spec</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP8" class="paragraph-anchor"></a><b>§8. </b>The following internal is just a shell for <s-descriptive-type-expression>,
|
|
but it temporarily changes the parsing mode to phrase token parsing, so that
|
|
kind variables will be read as formal prototypes.
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-phrase-token-type></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">int</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">s</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">kind_parsing_mode</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">kind_parsing_mode</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">PHRASE_TOKEN_KIND_PARSING</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">int</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">t</span><span class="Preform-plain-syntax"> = </span><span class="Preform-function-syntax"><s-descriptive-type-expression></span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">);</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">kind_parsing_mode</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">s</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">t</span><span class="Preform-plain-syntax">) { ==> { </span><span class="Preform-function-syntax"><<r>></span><span class="Preform-plain-syntax">, </span><span class="Preform-function-syntax"><<rp>></span><span class="Preform-plain-syntax"> }; </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">; }</span>
|
|
<span class="Preform-plain-syntax"> ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP9" class="paragraph-anchor"></a><b>§9. </b>That's it for the cached nonterminals, but we will also define a convenient
|
|
super-nonterminal which matches almost any meaningful reference to data, so
|
|
it's frequently used as a way of finding out whether a new name will clash
|
|
with some existing meaning.
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-type-expression-or-value></span><span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">::=</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-function-syntax"><s-type-expression></span><span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">|</span><span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">==></span><span class="Preform-plain-syntax"> { pass 1 }</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-function-syntax"><s-value></span><span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">==></span><span class="Preform-plain-syntax"> { pass 1 }</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP10" class="paragraph-anchor"></a><b>§10. </b>One further convenience is for text which describes an explicit action in
|
|
a noun-like way.
|
|
</p>
|
|
|
|
<pre class="Preform-displayed-code all-displayed-code code-font">
|
|
<span class="Preform-function-syntax"><s-explicit-action></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> #</span><span class="Preform-identifier-syntax">ifdef</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">IF_MODULE</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">parse_node</span><span class="Preform-plain-syntax"> *</span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">NULL</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">int</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">p</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">permit_trying_omission</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">permit_trying_omission</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-function-syntax"><s-condition-uncached></span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">)) </span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax"> = </span><span class="Preform-function-syntax"><<rp>></span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">permit_trying_omission</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">p</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax">) {</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><a href="14-rv.html#SP20" class="function-link"><span class="Preform-function-syntax">Rvalues::is_CONSTANT_of_kind</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax">, </span><span class="Preform-identifier-syntax">K_stored_action</span><span class="Preform-plain-syntax">)) {</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> } </span><span class="Preform-reserved-syntax">else</span><span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><a href="14-cn.html#SP10" class="function-link"><span class="Preform-function-syntax">Conditions::is_TEST_ACTION</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax">)) {</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax">-></span><span class="Preform-element-syntax">down</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> } </span><span class="Preform-reserved-syntax">else</span><span class="Preform-plain-syntax"> {</span>
|
|
<span class="Preform-plain-syntax"> ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> }</span>
|
|
<span class="Preform-plain-syntax"> }</span>
|
|
<span class="Preform-plain-syntax"> #</span><span class="Preform-identifier-syntax">endif</span>
|
|
<span class="Preform-plain-syntax"> ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
|
|
<span class="Preform-function-syntax"><s-constant-action></span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">internal</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">{</span>
|
|
<span class="Preform-plain-syntax"> #</span><span class="Preform-identifier-syntax">ifdef</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">IF_MODULE</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">parse_node</span><span class="Preform-plain-syntax"> *</span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">NULL</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">int</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">p</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">permit_trying_omission</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">permit_trying_omission</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">int</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">p2</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">permit_nonconstant_action_parameters</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">permit_nonconstant_action_parameters</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">FALSE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-function-syntax"><s-condition-uncached></span><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">W</span><span class="Preform-plain-syntax">)) </span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax"> = </span><span class="Preform-function-syntax"><<rp>></span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">permit_trying_omission</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">p</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">permit_nonconstant_action_parameters</span><span class="Preform-plain-syntax"> = </span><span class="Preform-identifier-syntax">p2</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax">) {</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><a href="14-rv.html#SP20" class="function-link"><span class="Preform-function-syntax">Rvalues::is_CONSTANT_of_kind</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax">, </span><span class="Preform-identifier-syntax">K_stored_action</span><span class="Preform-plain-syntax">)) {</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> } </span><span class="Preform-reserved-syntax">else</span><span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">if</span><span class="Preform-plain-syntax"> (</span><a href="14-cn.html#SP10" class="function-link"><span class="Preform-function-syntax">Conditions::is_TEST_ACTION</span></a><span class="Preform-plain-syntax">(</span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax">)) {</span>
|
|
<span class="Preform-plain-syntax"> ==> { -, </span><span class="Preform-identifier-syntax">S</span><span class="Preform-plain-syntax">-></span><span class="Preform-element-syntax">down</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> </span><span class="Preform-reserved-syntax">return</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">TRUE</span><span class="Preform-plain-syntax">;</span>
|
|
<span class="Preform-plain-syntax"> } </span><span class="Preform-reserved-syntax">else</span><span class="Preform-plain-syntax"> {</span>
|
|
<span class="Preform-plain-syntax"> ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax"> }</span>
|
|
<span class="Preform-plain-syntax"> }</span>
|
|
<span class="Preform-plain-syntax"> #</span><span class="Preform-identifier-syntax">endif</span>
|
|
<span class="Preform-plain-syntax"> ==> { </span><span class="Preform-identifier-syntax">fail</span><span class="Preform-plain-syntax"> </span><span class="Preform-identifier-syntax">nonterminal</span><span class="Preform-plain-syntax"> };</span>
|
|
<span class="Preform-plain-syntax">}</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This is <a href="../words-module/4-ap.html" class="internal">Preform grammar</a>, not regular C code.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP11" class="paragraph-anchor"></a><b>§11. The cache. </b>The above nonterminals are called pretty frequently on overlapping or
|
|
coinciding runs of text. Inform runs substantially faster if the results of
|
|
parsing the most recent expressions are cached; so, for instance, if Inform
|
|
parses the text in words 507 to 511 once, it need not do so again in the same
|
|
context.
|
|
</p>
|
|
|
|
<p class="commentary firstcommentary"><a id="SP12" class="paragraph-anchor"></a><b>§12. </b>The cache takes the form of a modest ring buffer for each of the contexts:
|
|
</p>
|
|
|
|
<pre class="definitions code-font"><span class="definition-keyword">define</span> <span class="Preform-constant-syntax">MAXIMUM_CACHE_SIZE</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">20</span><span class="Preform-plain-syntax"> </span><span class="Preform-comment-syntax"> a Goldilocks value: too high slows us down, too low doesn't cache enough</span>
|
|
<span class="definition-keyword">define</span> <span class="Preform-constant-syntax">NUMBER_OF_CACHED_NONTERMINALS</span><span class="Preform-plain-syntax"> </span><span class="Preform-constant-syntax">5</span>
|
|
</pre>
|
|
<pre class="displayed-code all-displayed-code code-font">
|
|
<span class="reserved-syntax">typedef</span><span class="plain-syntax"> </span><span class="reserved-syntax">struct</span><span class="plain-syntax"> </span><span class="reserved-syntax">expression_cache</span><span class="plain-syntax"> {</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">struct</span><span class="plain-syntax"> </span><span class="reserved-syntax">expression_cache_entry</span><span class="plain-syntax"> </span><span class="identifier-syntax">pe_cache</span><span class="plain-syntax">[</span><span class="constant-syntax">MAXIMUM_CACHE_SIZE</span><span class="plain-syntax">];</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">pe_cache_size</span><span class="plain-syntax">; </span><span class="comment-syntax"> number of entries used, 0 to </span><span class="extract"><span class="extract-syntax">MAXIMUM_CACHE_SIZE</span></span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">pe_cache_posn</span><span class="plain-syntax">; </span><span class="comment-syntax"> next write position, 0 to </span><span class="extract"><span class="extract-syntax">pe_cache_size</span></span><span class="comment-syntax"> minus 1</span>
|
|
<span class="plain-syntax">} </span><span class="reserved-syntax">expression_cache</span><span class="plain-syntax">;</span>
|
|
|
|
<span class="reserved-syntax">typedef</span><span class="plain-syntax"> </span><span class="reserved-syntax">struct</span><span class="plain-syntax"> </span><span class="reserved-syntax">expression_cache_entry</span><span class="plain-syntax"> {</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">struct</span><span class="plain-syntax"> </span><span class="identifier-syntax">wording</span><span class="plain-syntax"> </span><span class="identifier-syntax">cached_query</span><span class="plain-syntax">; </span><span class="comment-syntax"> the word range whose parsing this is</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">struct</span><span class="plain-syntax"> </span><span class="identifier-syntax">parse_node</span><span class="plain-syntax"> *</span><span class="identifier-syntax">cached_result</span><span class="plain-syntax">; </span><span class="comment-syntax"> and the result (quite possibly </span><span class="extract"><span class="extract-syntax">UNKNOWN_NT</span></span><span class="comment-syntax">)</span>
|
|
<span class="plain-syntax">} </span><span class="reserved-syntax">expression_cache_entry</span><span class="plain-syntax">;</span>
|
|
|
|
<span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">expression_cache_has_been_used</span><span class="plain-syntax"> = </span><span class="identifier-syntax">FALSE</span><span class="plain-syntax">;</span>
|
|
<span class="reserved-syntax">expression_cache</span><span class="plain-syntax"> </span><span class="identifier-syntax">contextual_cache</span><span class="plain-syntax">[</span><span class="constant-syntax">NUMBER_OF_CACHED_NONTERMINALS</span><span class="plain-syntax">];</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>The structure expression_cache is private to this section.</li><li>The structure expression_cache_entry is private to this section.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP13" class="paragraph-anchor"></a><b>§13. </b></p>
|
|
|
|
<pre class="displayed-code all-displayed-code code-font">
|
|
<span class="identifier-syntax">parse_node</span><span class="plain-syntax"> *</span><span class="function-syntax">ExParser::parse_with_cache</span><button class="popup" onclick="togglePopup('usagePopup1')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup1">Usage of <span class="code-font"><span class="function-syntax">ExParser::parse_with_cache</span></span>:<br/><a href="10-aots.html#SP3">§3</a>, <a href="10-aots.html#SP4">§4</a>, <a href="10-aots.html#SP5">§5</a>, <a href="10-aots.html#SP6">§6</a>, <a href="10-aots.html#SP7">§7</a></span></button><span class="plain-syntax">(</span><span class="identifier-syntax">wording</span><span class="plain-syntax"> </span><span class="identifier-syntax">W</span><span class="plain-syntax">, </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">context</span><span class="plain-syntax">, </span><span class="identifier-syntax">nonterminal</span><span class="plain-syntax"> *</span><span class="identifier-syntax">nt</span><span class="plain-syntax">) {</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">Wordings::empty</span><span class="plain-syntax">(</span><span class="identifier-syntax">W</span><span class="plain-syntax">)) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><a href="14-sp.html#SP10" class="function-link"><span class="function-syntax">Specifications::new_UNKNOWN</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">W</span><span class="plain-syntax">);</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">context</span><span class="plain-syntax"> < </span><span class="constant-syntax">0</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">context</span><span class="plain-syntax"> >= </span><span class="constant-syntax">NUMBER_OF_CACHED_NONTERMINALS</span><span class="plain-syntax">))</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">internal_error</span><span class="plain-syntax"> (</span><span class="string-syntax">"bad expression parsing context"</span><span class="plain-syntax">);</span>
|
|
<span class="plain-syntax"> </span><span class="named-paragraph-container code-font"><a href="10-aots.html#SP13_1" class="named-paragraph-link"><span class="named-paragraph">Check the expression cache to see if we already know the answer</span><span class="named-paragraph-number">13.1</span></a></span><span class="plain-syntax">;</span>
|
|
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">unwanted</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">; </span><span class="identifier-syntax">parse_node</span><span class="plain-syntax"> *</span><span class="identifier-syntax">spec</span><span class="plain-syntax"> = </span><span class="identifier-syntax">NULL</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">plm</span><span class="plain-syntax"> = </span><span class="identifier-syntax">preform_lookahead_mode</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">preform_lookahead_mode</span><span class="plain-syntax"> = </span><span class="identifier-syntax">FALSE</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">Preform::parse_nt_against_word_range</span><span class="plain-syntax">(</span><span class="identifier-syntax">nt</span><span class="plain-syntax">, </span><span class="identifier-syntax">W</span><span class="plain-syntax">, &</span><span class="identifier-syntax">unwanted</span><span class="plain-syntax">, (</span><span class="reserved-syntax">void</span><span class="plain-syntax"> **) &</span><span class="identifier-syntax">spec</span><span class="plain-syntax">)) {</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">Wordings::empty</span><span class="plain-syntax">(</span><span class="identifier-syntax">Node::get_text</span><span class="plain-syntax">(</span><span class="identifier-syntax">spec</span><span class="plain-syntax">))) </span><span class="identifier-syntax">Node::set_text</span><span class="plain-syntax">(</span><span class="identifier-syntax">spec</span><span class="plain-syntax">, </span><span class="identifier-syntax">W</span><span class="plain-syntax">);</span>
|
|
<span class="plain-syntax"> } </span><span class="reserved-syntax">else</span><span class="plain-syntax"> </span><span class="identifier-syntax">spec</span><span class="plain-syntax"> = </span><a href="14-sp.html#SP10" class="function-link"><span class="function-syntax">Specifications::new_UNKNOWN</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">W</span><span class="plain-syntax">);</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">preform_lookahead_mode</span><span class="plain-syntax"> = </span><span class="identifier-syntax">plm</span><span class="plain-syntax">;</span>
|
|
|
|
<span class="plain-syntax"> </span><span class="named-paragraph-container code-font"><a href="10-aots.html#SP13_2" class="named-paragraph-link"><span class="named-paragraph">Write the newly discovered specification to the cache for future use</span><span class="named-paragraph-number">13.2</span></a></span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">VerifyTree::verify_structure_from</span><span class="plain-syntax">(</span><span class="identifier-syntax">spec</span><span class="plain-syntax">);</span>
|
|
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">spec</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax">}</span>
|
|
</pre>
|
|
<p class="commentary firstcommentary"><a id="SP13_1" class="paragraph-anchor"></a><b>§13.1. </b>The following seeks a previously cached answer:
|
|
</p>
|
|
|
|
<p class="commentary"><span class="named-paragraph-container code-font"><span class="named-paragraph-defn">Check the expression cache to see if we already know the answer</span><span class="named-paragraph-number">13.1</span></span><span class="comment-syntax"> =</span>
|
|
</p>
|
|
|
|
<pre class="displayed-code all-displayed-code code-font">
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">expression_cache</span><span class="plain-syntax"> *</span><span class="identifier-syntax">ec</span><span class="plain-syntax"> = &(</span><span class="identifier-syntax">contextual_cache</span><span class="plain-syntax">[</span><span class="identifier-syntax">context</span><span class="plain-syntax">]);</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">expression_cache_has_been_used</span><span class="plain-syntax"> == </span><span class="identifier-syntax">FALSE</span><span class="plain-syntax">) {</span>
|
|
<span class="plain-syntax"> </span><a href="10-aots.html#SP14" class="function-link"><span class="function-syntax">ExParser::warn_expression_cache</span></a><span class="plain-syntax">(); </span><span class="comment-syntax"> this empties all the caches</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">expression_cache_has_been_used</span><span class="plain-syntax"> = </span><span class="identifier-syntax">TRUE</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> }</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax">=0; </span><span class="identifier-syntax">i</span><span class="function-syntax"><ec-></span><span class="element-syntax">pe_cache_size</span><span class="plain-syntax">; </span><span class="identifier-syntax">i</span><span class="plain-syntax">++)</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">Wordings::eq</span><span class="plain-syntax">(</span><span class="identifier-syntax">W</span><span class="plain-syntax">, </span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">].</span><span class="element-syntax">cached_query</span><span class="plain-syntax">))</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">].</span><span class="element-syntax">cached_result</span><span class="plain-syntax">;</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This code is used in <a href="10-aots.html#SP13">§13</a>.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP13_2" class="paragraph-anchor"></a><b>§13.2. </b>The cache expands until it reaches <span class="extract"><span class="extract-syntax">MAXIMUM_CACHE_SIZE</span></span>; after that,
|
|
entries are written in a position cycling through the ring. In either case
|
|
it takes <span class="extract"><span class="extract-syntax">MAXIMUM_CACHE_SIZE</span></span> further parses (not found in the cache) to
|
|
overwrite the one we put down now.
|
|
</p>
|
|
|
|
<p class="commentary"><span class="named-paragraph-container code-font"><span class="named-paragraph-defn">Write the newly discovered specification to the cache for future use</span><span class="named-paragraph-number">13.2</span></span><span class="comment-syntax"> =</span>
|
|
</p>
|
|
|
|
<pre class="displayed-code all-displayed-code code-font">
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">expression_cache</span><span class="plain-syntax"> *</span><span class="identifier-syntax">ec</span><span class="plain-syntax"> = &(</span><span class="identifier-syntax">contextual_cache</span><span class="plain-syntax">[</span><span class="identifier-syntax">context</span><span class="plain-syntax">]);</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache</span><span class="plain-syntax">[</span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache_posn</span><span class="plain-syntax">].</span><span class="element-syntax">cached_query</span><span class="plain-syntax"> = </span><span class="identifier-syntax">W</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache</span><span class="plain-syntax">[</span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache_posn</span><span class="plain-syntax">].</span><span class="element-syntax">cached_result</span><span class="plain-syntax"> = </span><span class="identifier-syntax">spec</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache_posn</span><span class="plain-syntax">++;</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache_size</span><span class="plain-syntax"> < </span><span class="constant-syntax">MAXIMUM_CACHE_SIZE</span><span class="plain-syntax">) </span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache_size</span><span class="plain-syntax">++;</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache_posn</span><span class="plain-syntax"> == </span><span class="constant-syntax">MAXIMUM_CACHE_SIZE</span><span class="plain-syntax">) </span><span class="identifier-syntax">ec</span><span class="plain-syntax">-></span><span class="element-syntax">pe_cache_posn</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
|
|
</pre>
|
|
<ul class="endnotetexts"><li>This code is used in <a href="10-aots.html#SP13">§13</a>.</li></ul>
|
|
<p class="commentary firstcommentary"><a id="SP14" class="paragraph-anchor"></a><b>§14. </b>As with all caches, we have to be careful that the information does not fall
|
|
out of date. There are two things which can go wrong: the S-node in the cache
|
|
might be altered, perhaps as a result of the type-checker trying to force a
|
|
round peg into a square hole; or the stock of Inform's defined names might
|
|
change, so that the same text now has to be read differently.
|
|
</p>
|
|
|
|
<p class="commentary">The first problem can't be fixed here. It's tempting to try something like
|
|
flagging S-nodes which have been altered, and then ensuring that the
|
|
cache never serves up an altered result. But that fails for timing reasons —
|
|
by the time the S-node might be altered, pointers to it may exist
|
|
in multiple data structures already, because the cache might have served
|
|
it more than once by that time. (Not just a theoretical possibility — tests
|
|
show that this does, albeit rarely, happen.) The brute force solution is to
|
|
serve a copy of the cache entry, and thus never send out the same pointer
|
|
twice. But this more than doubles the memory required to store S-nodes,
|
|
which is unacceptable, and also slows Inform down, because allocating memory
|
|
for all those copies is laborious. We therefore just have to be very careful
|
|
about modifying S-nodes which have arisen from parsing.
|
|
</p>
|
|
|
|
<p class="commentary">The second problem is easier. We require other parts of Inform which make
|
|
or unmake name definitions to warn us, by calling this routine. Definitions
|
|
are made and unmade relatively rarely, so the performance hit is small.
|
|
</p>
|
|
|
|
<pre class="displayed-code all-displayed-code code-font">
|
|
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">ExParser::warn_expression_cache</span><button class="popup" onclick="togglePopup('usagePopup2')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup2">Usage of <span class="code-font"><span class="function-syntax">ExParser::warn_expression_cache</span></span>:<br/><a href="10-aots.html#SP13_1">§13.1</a><br/>Local Variables - <a href="24-lv.html#SP6">§6</a>, <a href="24-lv.html#SP40">§40</a><br/>Parse Invocations - <a href="25-pi.html#SP5">§5</a><br/>Compile Phrases - <a href="25-cp.html#SP2_2">§2.2</a></span></button><span class="plain-syntax">(</span><span class="reserved-syntax">void</span><span class="plain-syntax">) {</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax">=0; </span><span class="identifier-syntax">i</span><span class="plain-syntax"><</span><span class="constant-syntax">NUMBER_OF_CACHED_NONTERMINALS</span><span class="plain-syntax">; </span><span class="identifier-syntax">i</span><span class="plain-syntax">++) {</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">contextual_cache</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">].</span><span class="element-syntax">pe_cache_size</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">contextual_cache</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">].</span><span class="element-syntax">pe_cache_posn</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> }</span>
|
|
<span class="plain-syntax">}</span>
|
|
</pre>
|
|
<p class="commentary firstcommentary"><a id="SP15" class="paragraph-anchor"></a><b>§15. Void phrases. </b>The S-parser is also used by the main code compiler to turn phrases into
|
|
S-nodes, using <s-command> and <s-say-command>. These however need a
|
|
wrapper: instead of turning text into an S-node, we take text from an
|
|
existing node (in the structural parse tree for a routine), turn that
|
|
into a new S-node with an invocation list below it, then glue the list
|
|
back into the original tree but throw away the S-node head.
|
|
</p>
|
|
|
|
<pre class="displayed-code all-displayed-code code-font">
|
|
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">ExParser::parse_void_phrase</span><button class="popup" onclick="togglePopup('usagePopup3')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup3">Usage of <span class="code-font"><span class="function-syntax">ExParser::parse_void_phrase</span></span>:<br/>Compile Phrases - <a href="25-cp.html#SP5">§5</a></span></button><span class="plain-syntax">(</span><span class="identifier-syntax">parse_node</span><span class="plain-syntax"> *</span><span class="identifier-syntax">p</span><span class="plain-syntax">) {</span>
|
|
<span class="plain-syntax"> </span><a href="10-aots.html#SP15" class="function-link"><span class="function-syntax">ExParser::parse_phrase_inner</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">p</span><span class="plain-syntax">, </span><span class="identifier-syntax">FALSE</span><span class="plain-syntax">);</span>
|
|
<span class="plain-syntax">}</span>
|
|
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">ExParser::parse_say_term</span><button class="popup" onclick="togglePopup('usagePopup4')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup4">Usage of <span class="code-font"><span class="function-syntax">ExParser::parse_say_term</span></span>:<br/>Compile Phrases - <a href="25-cp.html#SP4_2_1">§4.2.1</a></span></button><span class="plain-syntax">(</span><span class="identifier-syntax">parse_node</span><span class="plain-syntax"> *</span><span class="identifier-syntax">p</span><span class="plain-syntax">) {</span>
|
|
<span class="plain-syntax"> </span><a href="10-aots.html#SP15" class="function-link"><span class="function-syntax">ExParser::parse_phrase_inner</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">p</span><span class="plain-syntax">, </span><span class="identifier-syntax">TRUE</span><span class="plain-syntax">);</span>
|
|
<span class="plain-syntax">}</span>
|
|
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">ExParser::parse_phrase_inner</span><span class="plain-syntax">(</span><span class="identifier-syntax">parse_node</span><span class="plain-syntax"> *</span><span class="identifier-syntax">p</span><span class="plain-syntax">, </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">as_say_term</span><span class="plain-syntax">) {</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">p</span><span class="plain-syntax"> == </span><span class="identifier-syntax">NULL</span><span class="plain-syntax">) </span><span class="identifier-syntax">internal_error</span><span class="plain-syntax">(</span><span class="string-syntax">"no node to parse"</span><span class="plain-syntax">);</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">p</span><span class="plain-syntax">-></span><span class="element-syntax">down</span><span class="plain-syntax"> = </span><span class="identifier-syntax">NULL</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">Wordings::nonempty</span><span class="plain-syntax">(</span><span class="identifier-syntax">Node::get_text</span><span class="plain-syntax">(</span><span class="identifier-syntax">p</span><span class="plain-syntax">))) {</span>
|
|
<span class="plain-syntax"> </span><span class="identifier-syntax">parse_node</span><span class="plain-syntax"> *</span><span class="identifier-syntax">results</span><span class="plain-syntax"> = </span><span class="identifier-syntax">NULL</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">as_say_term</span><span class="plain-syntax"> == </span><span class="identifier-syntax">FALSE</span><span class="plain-syntax">) && (</span><span class="function-syntax"><s-command></span><span class="plain-syntax">(</span><span class="identifier-syntax">Node::get_text</span><span class="plain-syntax">(</span><span class="identifier-syntax">p</span><span class="plain-syntax">)))) </span><span class="identifier-syntax">results</span><span class="plain-syntax"> = </span><span class="function-syntax"><<rp>></span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">as_say_term</span><span class="plain-syntax">) && (</span><span class="function-syntax"><s-say-command></span><span class="plain-syntax">(</span><span class="identifier-syntax">Node::get_text</span><span class="plain-syntax">(</span><span class="identifier-syntax">p</span><span class="plain-syntax">)))) </span><span class="identifier-syntax">results</span><span class="plain-syntax"> = </span><span class="function-syntax"><<rp>></span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">results</span><span class="plain-syntax">) && (</span><span class="identifier-syntax">results</span><span class="plain-syntax">-></span><span class="element-syntax">down</span><span class="plain-syntax">)) </span><span class="identifier-syntax">p</span><span class="plain-syntax">-></span><span class="element-syntax">down</span><span class="plain-syntax"> = </span><span class="identifier-syntax">results</span><span class="plain-syntax">-></span><span class="element-syntax">down</span><span class="plain-syntax">-></span><span class="element-syntax">down</span><span class="plain-syntax">;</span>
|
|
<span class="plain-syntax"> }</span>
|
|
<span class="plain-syntax">}</span>
|
|
</pre>
|
|
<nav role="progress"><div class="progresscontainer">
|
|
<ul class="progressbar"><li class="progressprev"><a href="10-its.html">❮</a></li><li class="progresschapter"><a href="P-wtmd.html">P</a></li><li class="progresschapter"><a href="1-cm.html">1</a></li><li class="progresschapter"><a href="2-up.html">2</a></li><li class="progresschapter"><a href="3-bv.html">3</a></li><li class="progresschapter"><a href="4-dlr.html">4</a></li><li class="progresschapter"><a href="5-rpt.html">5</a></li><li class="progresschapter"><a href="6-lp.html">6</a></li><li class="progresschapter"><a href="7-am.html">7</a></li><li class="progresschapter"><a href="8-ptu.html">8</a></li><li class="progresschapter"><a href="9-ef.html">9</a></li><li class="progresscurrentchapter">10</li><li class="progresssection"><a href="10-its.html">its</a></li><li class="progresscurrent">aots</li><li class="progresssection"><a href="10-pl.html">pl</a></li><li class="progresssection"><a href="10-cad.html">cad</a></li><li class="progresssection"><a href="10-teav.html">teav</a></li><li class="progresssection"><a href="10-varc.html">varc</a></li><li class="progresssection"><a href="10-cap.html">cap</a></li><li class="progresschapter"></li><li class="progresschapter"><a href="12-terr.html">12</a></li><li class="progresschapter"><a href="13-kak.html">13</a></li><li class="progresschapter"><a href="14-sp.html">14</a></li><li class="progresschapter"><a href="15-pr.html">15</a></li><li class="progresschapter"><a href="16-is.html">16</a></li><li class="progresschapter"><a href="17-tl.html">17</a></li><li class="progresschapter"><a href="18-lc.html">18</a></li><li class="progresschapter"><a href="19-tc.html">19</a></li><li class="progresschapter"><a href="20-eq.html">20</a></li><li class="progresschapter"><a href="21-rl.html">21</a></li><li class="progresschapter"><a href="22-itp.html">22</a></li><li class="progresschapter"><a href="23-ad.html">23</a></li><li class="progresschapter"><a href="24-lv.html">24</a></li><li class="progresschapter"><a href="25-in.html">25</a></li><li class="progresschapter"><a href="26-fc.html">26</a></li><li class="progresschapter"><a href="27-hr.html">27</a></li><li class="progressnext"><a href="10-pl.html">❯</a></li></ul></div>
|
|
</nav><!--End of weave-->
|
|
|
|
</main>
|
|
</body>
|
|
</html>
|
|
|