1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-08 18:14:21 +03:00
inform7/docs/standard_rules/S-pt.html

4469 lines
263 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>S/ot3</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Language" content="en-gb">
<link href="inweb.css" rel="stylesheet" rev="stylesheet" type="text/css">
</head>
<body>
<!--Weave of 'S/pt' generated by 7-->
<ul class="crumbs"><li><a href="../webs.html">&#9733;</a></li><li><a href="index.html">standard_rules Template Library</a></li><li><b>Parser Template</b></li></ul><p class="purpose">The parser for turning the text of the typed command into a proposed action by the player.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. Grammar Line Variables</a></li><li><a href="#SP2">&#167;2. Grammar Token Variables</a></li><li><a href="#SP3">&#167;3. Match List Variables</a></li><li><a href="#SP4">&#167;4. Words</a></li><li><a href="#SP5">&#167;5. Snippets</a></li><li><a href="#SP6">&#167;6. Unpacking Grammar Lines</a></li><li><a href="#SP7">&#167;7. Keyboard Primitive</a></li><li><a href="#SP8">&#167;8. Reading the Command</a></li><li><a href="#SP9">&#167;9. Parser Proper</a></li><li><a href="#SP10">&#167;10. Parser Letter A</a></li><li><a href="#SP11">&#167;11. Parser Letter B</a></li><li><a href="#SP12">&#167;12. Parser Letter C</a></li><li><a href="#SP13">&#167;13. Parser Letter D</a></li><li><a href="#SP14">&#167;14. Parser Letter E</a></li><li><a href="#SP15">&#167;15. Parser Letter F</a></li><li><a href="#SP16">&#167;16. Parser Letter G</a></li><li><a href="#SP17">&#167;17. Parser Letter H</a></li><li><a href="#SP18">&#167;18. Parser Letter I</a></li><li><a href="#SP19">&#167;19. Parser Letter J</a></li><li><a href="#SP20">&#167;20. Parser Letter K</a></li><li><a href="#SP21">&#167;21. End of Parser Proper</a></li><li><a href="#SP22">&#167;22. Internal Rule</a></li><li><a href="#SP23">&#167;23. Parse Token</a></li><li><a href="#SP24">&#167;24. Parse Token Letter A</a></li><li><a href="#SP25">&#167;25. Parse Token Letter B</a></li><li><a href="#SP26">&#167;26. Parse Token Letter C</a></li><li><a href="#SP27">&#167;27. Parse Token Letter D</a></li><li><a href="#SP28">&#167;28. Parse Token Letter E</a></li><li><a href="#SP29">&#167;29. Parse Token Letter F</a></li><li><a href="#SP30">&#167;30. Descriptors</a></li><li><a href="#SP31">&#167;31. Parsing Descriptors</a></li><li><a href="#SP32">&#167;32. Preposition Chain</a></li><li><a href="#SP33">&#167;33. Creature</a></li><li><a href="#SP34">&#167;34. Noun Domain</a></li><li><a href="#SP35">&#167;35. Adjudicate</a></li><li><a href="#SP36">&#167;36. ReviseMulti</a></li><li><a href="#SP37">&#167;37. Match List</a></li><li><a href="#SP38">&#167;38. ScoreMatchL</a></li><li><a href="#SP39">&#167;39. BestGuess</a></li><li><a href="#SP40">&#167;40. SingleBestGuess</a></li><li><a href="#SP41">&#167;41. Identical</a></li><li><a href="#SP42">&#167;42. Print Command</a></li><li><a href="#SP43">&#167;43. CantSee</a></li><li><a href="#SP44">&#167;44. Multiple Object List</a></li><li><a href="#SP45">&#167;45. Scope</a></li><li><a href="#SP46">&#167;46. Scope Level 0</a></li><li><a href="#SP47">&#167;47. SearchScope</a></li><li><a href="#SP48">&#167;48. ScopeWithin</a></li><li><a href="#SP49">&#167;49. DoScopeActionAndRecurse</a></li><li><a href="#SP50">&#167;50. DoScopeAction</a></li><li><a href="#SP51">&#167;51. Parsing Object Names</a></li><li><a href="#SP52">&#167;52. TryGivenObject</a></li><li><a href="#SP53">&#167;53. Refers</a></li><li><a href="#SP54">&#167;54. NounWord</a></li><li><a href="#SP55">&#167;55. TryNumber</a></li><li><a href="#SP56">&#167;56. Gender</a></li><li><a href="#SP57">&#167;57. Noticing Plurals</a></li><li><a href="#SP58">&#167;58. Pronoun Handling</a></li><li><a href="#SP59">&#167;59. Yes/No Questions</a></li><li><a href="#SP60">&#167;60. Number Words</a></li><li><a href="#SP61">&#167;61. Choose Objects</a></li><li><a href="#SP62">&#167;62. Default Topic</a></li><li><a href="#SP63">&#167;63. Recognition-only-GPR</a></li><li><a href="#SP64">&#167;64. RunRoutines</a></li><li><a href="#SP65">&#167;65. Setting the Player's Command</a></li><li><a href="#SP66">&#167;66. Multiple Object List</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. Grammar Line Variables. </b>This is the I6 library parser in mostly untouched form: reformatted for template
file use, and with paragraph divisions, but otherwise hardly changed at all.
It is a complex algorithm but one which is known to produce good results for
the most part, and it is well understood from (at time of writing) fifteen
years of use. A few I7 additions have been made, but none disrupting the
basic method. For instance, I7's system for resolving ambiguities is
implemented by providing a <code class="display"><span class="extract">ChooseObjects</span></code> routine, just as a user of the
I6 library would do.
</p>
<p class="inwebparagraph">The I6 parser uses a huge number of global variables, which is not to modern
programming tastes: in the early days of Inform, the parser was essentially
written in assembly-language only lightly structured by C-like syntaxes,
and the Z-machine's 240 globals were more or less registers. The I6 library
made no distinction between which were "private" to the parser and which
allowed to be accessed by the user's code at large. The I7 template does
impose that boundary, though not very strongly: the variables defined
in "Output.i6t" are for general access, while the ones below should only
be read or written by the parser.
</p>
<pre class="display">
<span class="plain">Global best_etype; ! Preferred error number so far</span>
<span class="plain">Global nextbest_etype; ! Preferred one, if ASKSCOPE_PE disallowed</span>
<span class="plain">Global parser_inflection; ! A property (usually "name") to find object names in</span>
<span class="plain">Array pattern --&gt; 32; ! For the current pattern match</span>
<span class="plain">Global pcount; ! and a marker within it</span>
<span class="plain">Array pattern2 --&gt; 32; ! And another, which stores the best match</span>
<span class="plain">Global pcount2; ! so far</span>
<span class="plain">Array line_ttype--&gt;32; ! For storing an analysed grammar line</span>
<span class="plain">Array line_tdata--&gt;32;</span>
<span class="plain">Array line_token--&gt;32;</span>
<span class="plain">Global nsns; ! Number of special_numbers entered so far</span>
<span class="plain">Global params_wanted; ! Number of parameters needed (which may change in parsing)</span>
<span class="plain">Global inferfrom; ! The point from which the rest of the command must be inferred</span>
<span class="plain">Global inferword; ! And the preposition inferred</span>
<span class="plain">Global dont_infer; ! Another dull flag</span>
<span class="plain">Global cobj_flag = 0;</span>
<span class="plain">Global oops_from; ! The "first mistake" word number</span>
<span class="plain">Global saved_oops; ! Used in working this out</span>
<span class="plain">Array oops_workspace -&gt; 64; ! Used temporarily by "oops" routine</span>
<span class="plain">Global held_back_mode; ! Flag: is there some input from last time</span>
<span class="plain">Global hb_wn; ! left over? (And a save value for wn.)</span>
<span class="plain">! (Used for full stops and "then".)</span>
<span class="plain">Global usual_grammar_after; ! Point from which usual grammar is parsed (it may vary from</span>
<span class="plain">! the above if user's routines match multi-word verbs)</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. Grammar Token Variables. </b>More globals, but dealing at the level of individual tokens now.
</p>
<pre class="display">
<span class="plain">Constant PATTERN_NULL = $ffff; ! Entry for a token producing no text</span>
<span class="plain">Global found_ttype; ! Used to break up tokens into type</span>
<span class="plain">Global found_tdata; ! and data (by AnalyseToken)</span>
<span class="plain">Global token_filter; ! For noun filtering by user routines</span>
<span class="plain">Global length_of_noun; ! Set by NounDomain to no of words in noun</span>
<span class="plain">Global lookahead; ! The token after the one now being matched</span>
<span class="plain">Global multi_mode; ! Multiple mode</span>
<span class="plain">Global multi_wanted; ! Number of things needed in multitude</span>
<span class="plain">Global multi_had; ! Number of things actually found</span>
<span class="plain">Global multi_context; ! What token the multi-obj was accepted for</span>
<span class="plain">Global indef_mode; ! "Indefinite" mode - ie, "take a brick"</span>
<span class="plain">! is in this mode</span>
<span class="plain">Global indef_type; ! Bit-map holding types of specification</span>
<span class="plain">Global indef_wanted; ! Number of items wanted (INDEF_ALL_WANTED for all)</span>
<span class="plain">Constant INDEF_ALL_WANTED = 32767;</span>
<span class="plain">Global indef_guess_p; ! Plural-guessing flag</span>
<span class="plain">Global indef_owner; ! Object which must hold these items</span>
<span class="plain">Global indef_cases; ! Possible gender and numbers of them</span>
<span class="plain">Global indef_possambig; ! Has a possibly dangerous assumption</span>
<span class="plain">! been made about meaning of a descriptor?</span>
<span class="plain">Global indef_nspec_at; ! Word at which a number like "two" was parsed</span>
<span class="plain">! (for backtracking)</span>
<span class="plain">Global allow_plurals; ! Whether plurals presently allowed or not</span>
<span class="plain">Global take_all_rule; ! Slightly different rules apply to "take all" than other uses</span>
<span class="plain">! of multiple objects, to make adjudication produce more</span>
<span class="plain">! pragmatically useful results</span>
<span class="plain">! (Not a flag: possible values 0, 1, 2)</span>
<span class="plain">Global dict_flags_of_noun; ! Of the noun currently being parsed</span>
<span class="plain">! (a bitmap in #dict_par1 format)</span>
<span class="plain">Global pronoun__word; ! Saved value</span>
<span class="plain">Global pronoun__obj; ! Saved value</span>
<span class="plain">Constant comma_word = 'comma,'; ! An "untypeable word" used to substitute</span>
<span class="plain">! for commas in parse buffers</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. Match List Variables. </b>The most difficult tokens to match are those which refer to objects, since
there is such a variety of names which can be given to any individual object,
and we don't of course know which object or objects are meant. We store the
possibilities (up to <code class="display"><span class="extract">MATCH_LIST_WORDS</span></code>, anyway) in a data structure called the match list.
</p>
<pre class="display">
<span class="plain">Array match_list --&gt; MATCH_LIST_WORDS; ! An array of matched objects so far</span>
<span class="plain">Array match_classes --&gt; MATCH_LIST_WORDS; ! An array of equivalence classes for them</span>
<span class="plain">Array match_scores --&gt; MATCH_LIST_WORDS; ! An array of match scores for them</span>
<span class="plain">Global number_matched; ! How many items in it? (0 means none)</span>
<span class="plain">Global number_of_classes; ! How many equivalence classes?</span>
<span class="plain">Global match_length; ! How many words long are these matches?</span>
<span class="plain">Global match_from; ! At what word of the input do they begin?</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. Words. </b>The player's command is broken down into a numbered sequence of words, which
break at spaces or certain punctuation (see the DM4). The numbering runs
upwards from 1 to <code class="display"><span class="extract">WordCount()</span></code>. The following utility routines provide
access to words in the current command; because buffers have different
definitions in Z and Glulx, so these routines must vary also.
</p>
<p class="inwebparagraph">The actual text of each word is stored as a sequence of ZSCII values in
a <code class="display"><span class="extract">-&gt;</span></code> (byte) array, with address <code class="display"><span class="extract">WordAddress(x)</span></code> and length <code class="display"><span class="extract">WordLength(x)</span></code>.
</p>
<p class="inwebparagraph">We picture the command as a stream of words to be read one at a time, with
the global variable <code class="display"><span class="extract">wn</span></code> being the "current word" marker. <code class="display"><span class="extract">NextWord</span></code>, which
takes no arguments, returns:
</p>
<ul class="items"><li>(a) 0 if the word at <code class="display"><span class="extract">wn</span></code> is unrecognised by the dictionary or <code class="display"><span class="extract">wn</span></code> is out
of range,
</li><li>(b) <code class="display"><span class="extract">comma_word</span></code> if the word was a comma,
</li><li>(c) <code class="display"><span class="extract">THEN1__WD</span></code> if it was a full stop (because of the Infocom tradition that
a full stop abbreviates for the word "then": e.g., TAKE BOX. EAST was read
as two commands in succession),
</li><li>(d) or the dictionary address if the word was recognised.
</li></ul>
<p class="inwebparagraph">The current word marker <code class="display"><span class="extract">wn</span></code> is always advanced.
</p>
<p class="inwebparagraph"><code class="display"><span class="extract">NextWordStopped</span></code> does the same, but returns -1 when <code class="display"><span class="extract">wn</span></code> is out of range
(e.g., by having advanced past the last word in the command).
</p>
<pre class="display">
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">[ WordCount; return parse-&gt;1; ];</span>
<span class="plain">[ WordAddress wordnum; return buffer + parse-&gt;(wordnum*4+1); ];</span>
<span class="plain">[ WordLength wordnum; return parse-&gt;(wordnum*4); ];</span>
<span class="plain">#Ifnot;</span>
<span class="plain">[ WordCount; return parse--&gt;0; ];</span>
<span class="plain">[ WordAddress wordnum; return buffer + parse--&gt;(wordnum*3); ];</span>
<span class="plain">[ WordLength wordnum; return parse--&gt;(wordnum*3 - 1); ];</span>
<span class="plain">#Endif;</span>
<span class="plain">[ WordFrom w p i j wc;</span>
<span class="plain">#Ifdef TARGET_ZCODE; wc = p-&gt;1; i = w*2-1;</span>
<span class="plain">#Ifnot; wc = p--&gt;0; i = w*3-2; #Endif;</span>
<span class="plain">if ((w &lt; 1) || (w &gt; wc)) return 0;</span>
<span class="plain">j = p--&gt;i;</span>
<span class="plain">if (j == ',//') j = comma_word;</span>
<span class="plain">if (j == './/') j = THEN1__WD;</span>
<span class="plain">return j;</span>
<span class="plain">];</span>
<span class="plain">[ NextWord i j wc;</span>
<span class="plain">#Ifdef TARGET_ZCODE; wc = parse-&gt;1; i = wn*2-1;</span>
<span class="plain">#Ifnot; wc = parse--&gt;0; i = wn*3-2; #Endif;</span>
<span class="plain">wn++;</span>
<span class="plain">if ((wn &lt; 2) || (wn &gt; wc+1)) return 0;</span>
<span class="plain">j = parse--&gt;i;</span>
<span class="plain">if (j == ',//') j = comma_word;</span>
<span class="plain">if (j == './/') j = THEN1__WD;</span>
<span class="plain">return j;</span>
<span class="plain">];</span>
<span class="plain">[ NextWordStopped wc;</span>
<span class="plain">#Ifdef TARGET_ZCODE; wc = parse-&gt;1; #Ifnot; wc = parse--&gt;0; #Endif;</span>
<span class="plain">if ((wn &lt; 1) || (wn &gt; wc)) { wn++; return -1; }</span>
<span class="plain">return NextWord();</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. Snippets. </b>Although the idea is arguably implicit in I6, the formal concept of
"snippet" is new in I7. A snippet is a value which represents a word
range in the command most recently typed by the player. These words number
consecutively upwards from 1, as noted above. The correspondence between
(w_1, w_2), the word range, and V, the number used to represent it as
an I6 value, is:
V = 100w_1 + (w_2-w_1+1)
so that the remainder mod 100 is the number of words in the range. We
require that 1&lt;= w_1&lt;= w_2&lt;= N, where N is the number of words in
the current player's command. The entire command is therefore represented by:
C = 100 + N
</p>
<pre class="display">
<span class="plain">[ PrintSnippet snip from to i w1 w2;</span>
<span class="plain">w1 = snip/100; w2 = w1 + (snip%100) - 1;</span>
<span class="plain">if ((w2&lt;w1) || (w1&lt;1) || (w2&gt;WordCount())) {</span>
<span class="plain">if ((w1 == 1) &amp;&amp; (w2 == 0)) rfalse;</span>
<span class="plain">return RunTimeProblem(RTP_SAYINVALIDSNIPPET, w1, w2);</span>
<span class="plain">}</span>
<span class="plain">from = WordAddress(w1); to = WordAddress(w2) + WordLength(w2) - 1;</span>
<span class="plain">for (i=from: i&lt;=to: i++) print (char) i-&gt;0;</span>
<span class="plain">];</span>
<span class="plain">[ SpliceSnippet snip t i w1 w2 nextw at endsnippet newlen;</span>
<span class="plain">w1 = snip/100; w2 = w1 + (snip%100) - 1;</span>
<span class="plain">if ((w2&lt;w1) || (w1&lt;1)) {</span>
<span class="plain">if ((w1 == 1) &amp;&amp; (w2 == 0)) return;</span>
<span class="plain">return RunTimeProblem(RTP_SPLICEINVALIDSNIPPET, w1, w2);</span>
<span class="plain">}</span>
<span class="plain">@push say__p; @push say__pc;</span>
<span class="plain">nextw = w2 + 1;</span>
<span class="plain">at = WordAddress(w1) - buffer;</span>
<span class="plain">if (nextw &lt;= WordCount()) endsnippet = 100*nextw + (WordCount() - nextw + 1);</span>
<span class="plain">buffer2--&gt;0 = 120;</span>
<span class="plain">newlen = VM_PrintToBuffer(buffer2, 120, SpliceSnippet__TextPrinter, t, endsnippet);</span>
<span class="plain">for (i=0: (i&lt;newlen) &amp;&amp; (at+i&lt;120): i++) buffer-&gt;(at+i) = buffer2-&gt;(WORDSIZE+i);</span>
<span class="plain">#Ifdef TARGET_ZCODE; buffer-&gt;1 = at+i; #ifnot; buffer--&gt;0 = at+i; #endif;</span>
<span class="plain">for (:at+i&lt;120:i++) buffer-&gt;(at+i) = ' ';</span>
<span class="plain">VM_Tokenise(buffer, parse);</span>
<span class="plain">players_command = 100 + WordCount();</span>
<span class="plain">@pull say__pc; @pull say__p;</span>
<span class="plain">];</span>
<span class="plain">[ SpliceSnippet__TextPrinter t endsnippet;</span>
<span class="plain">TEXT_TY_Say(t);</span>
<span class="plain">if (endsnippet) { print " "; PrintSnippet(endsnippet); }</span>
<span class="plain">];</span>
<span class="plain">[ SnippetIncludes test snippet w1 w2 wlen i j;</span>
<span class="plain">w1 = snippet/100; w2 = w1 + (snippet%100) - 1;</span>
<span class="plain">if ((w2&lt;w1) || (w1&lt;1)) {</span>
<span class="plain">if ((w1 == 1) &amp;&amp; (w2 == 0)) rfalse;</span>
<span class="plain">return RunTimeProblem(RTP_INCLUDEINVALIDSNIPPET, w1, w2);</span>
<span class="plain">}</span>
<span class="plain">if (metaclass(test) == Routine) {</span>
<span class="plain">wlen = snippet%100;</span>
<span class="plain">for (i=w1, j=wlen: j&gt;0: i++, j--) {</span>
<span class="plain">if (((test)(i, 0)) ~= GPR_FAIL) return i*100+wn-i;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">rfalse;</span>
<span class="plain">];</span>
<span class="plain">[ SnippetMatches snippet topic_gpr rv;</span>
<span class="plain">wn=1;</span>
<span class="plain">if (topic_gpr == 0) rfalse;</span>
<span class="plain">if (metaclass(topic_gpr) == Routine) {</span>
<span class="plain">rv = (topic_gpr)(snippet/100, snippet%100);</span>
<span class="plain">if (rv ~= GPR_FAIL) rtrue;</span>
<span class="plain">rfalse;</span>
<span class="plain">}</span>
<span class="plain">RunTimeProblem(RTP_BADTOPIC);</span>
<span class="plain">rfalse;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP6"></a><b>&#167;6. Unpacking Grammar Lines. </b>Grammar lines are sequences of tokens in an array built into the story file,
but in a format which differs depending on the virtual machine in use, so
the following code unpacks the data into more convenient if larger arrays
which are VM-independent.
</p>
<pre class="display">
<span class="plain">[ UnpackGrammarLine line_address i size;</span>
<span class="plain">for (i=0 : i&lt;32 : i++) {</span>
<span class="plain">line_token--&gt;i = ENDIT_TOKEN;</span>
<span class="plain">line_ttype--&gt;i = ELEMENTARY_TT;</span>
<span class="plain">line_tdata--&gt;i = ENDIT_TOKEN;</span>
<span class="plain">}</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">action_to_be = 256*(line_address-&gt;0) + line_address-&gt;1;</span>
<span class="plain">action_reversed = ((action_to_be &amp; $400) ~= 0);</span>
<span class="plain">action_to_be = action_to_be &amp; $3ff;</span>
<span class="plain">line_address--;</span>
<span class="plain">size = 3;</span>
<span class="plain">#Ifnot; ! GLULX</span>
<span class="plain">@aloads line_address 0 action_to_be;</span>
<span class="plain">action_reversed = (((line_address-&gt;2) &amp; 1) ~= 0);</span>
<span class="plain">line_address = line_address - 2;</span>
<span class="plain">size = 5;</span>
<span class="plain">#Endif;</span>
<span class="plain">params_wanted = 0;</span>
<span class="plain">for (i=0 : : i++) {</span>
<span class="plain">line_address = line_address + size;</span>
<span class="plain">if (line_address-&gt;0 == ENDIT_TOKEN) break;</span>
<span class="plain">line_token--&gt;i = line_address;</span>
<span class="plain">AnalyseToken(line_address);</span>
<span class="plain">if (found_ttype ~= PREPOSITION_TT) params_wanted++;</span>
<span class="plain">line_ttype--&gt;i = found_ttype;</span>
<span class="plain">line_tdata--&gt;i = found_tdata;</span>
<span class="plain">}</span>
<span class="plain">return line_address + 1;</span>
<span class="plain">];</span>
<span class="plain">[ AnalyseToken token;</span>
<span class="plain">if (token == ENDIT_TOKEN) {</span>
<span class="plain">found_ttype = ELEMENTARY_TT;</span>
<span class="plain">found_tdata = ENDIT_TOKEN;</span>
<span class="plain">return;</span>
<span class="plain">}</span>
<span class="plain">found_ttype = (token-&gt;0) &amp; $$1111;</span>
<span class="plain">found_tdata = (token+1)--&gt;0;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP7"></a><b>&#167;7. Keyboard Primitive. </b>This is the primitive routine to read from the keyboard: it usually delegates
this to a routine specific to the virtual machine being used, but sometimes
uses a hacked version to allow TEST commands to work. (When a TEST is running,
the text in the walk-through provided is fed into the buffer as if it had
been typed at the keyboard.)
</p>
<pre class="display">
<span class="plain">[ KeyboardPrimitive a_buffer a_table;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">return TestKeyboardPrimitive(a_buffer, a_table);</span>
<span class="plain">#Endif;</span>
<span class="plain">return VM_ReadKeyboard(a_buffer, a_table);</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP8"></a><b>&#167;8. Reading the Command. </b>The <code class="display"><span class="extract">Keyboard</span></code> routine actually receives the player's words, putting the
words in <code class="display"><span class="extract">a_buffer</span></code> and their dictionary addresses in <code class="display"><span class="extract">a_table</span></code>. It is
assumed that the table is the same one on each (standard) call. Much
of the code handles the OOPS and UNDO commands, which are not actions and
do not pass through the rest of the parser. The undo state is saved &mdash;
it is essentially an internal saved game, in the VM interpreter's memory
rather than in an external file &mdash; and note that this is therefore also
where execution picks up if an UNDO has been typed. Since UNDO recreates
the former machine state perfectly, it might seem impossible to tell that
an UNDO had occurred, but in fact the VM passes information back in the
form of a return code from the relevant instruction, and this allows us
to detect an undo. (We deal with it by printing the current location and
asking another command.)
</p>
<p class="inwebparagraph"><code class="display"><span class="extract">Keyboard</span></code> can also be used by miscellaneous routines in the game to ask
yes/no questions and the like, without invoking the rest of the parser.
</p>
<p class="inwebparagraph">The return value is the number of words typed.
</p>
<pre class="display">
<span class="plain">[ Keyboard a_buffer a_table nw i w w2 x1 x2;</span>
<span class="plain">sline1 = score; sline2 = turns;</span>
<span class="plain">while (true) {</span>
<span class="plain">! Save the start of the buffer, in case "oops" needs to restore it</span>
<span class="plain">for (i=0 : i&lt;64 : i++) oops_workspace-&gt;i = a_buffer-&gt;i;</span>
<span class="plain">! In case of an array entry corruption that shouldn't happen, but would be</span>
<span class="plain">! disastrous if it did:</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">a_buffer-&gt;0 = INPUT_BUFFER_LEN;</span>
<span class="plain">a_table-&gt;0 = 15; ! Allow to split input into this many words</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">! Print the prompt, and read in the words and dictionary addresses</span>
<span class="plain">PrintPrompt();</span>
<span class="plain">DrawStatusLine();</span>
<span class="plain">KeyboardPrimitive(a_buffer, a_table);</span>
<span class="plain">! Set nw to the number of words</span>
<span class="plain">#Ifdef TARGET_ZCODE; nw = a_table-&gt;1; #Ifnot; nw = a_table--&gt;0; #Endif;</span>
<span class="plain">! If the line was blank, get a fresh line</span>
<span class="plain">if (nw == 0) {</span>
<span class="plain">@push etype; etype = BLANKLINE_PE;</span>
<span class="plain">players_command = 100;</span>
<span class="plain">BeginActivity(PRINTING_A_PARSER_ERROR_ACT);</span>
<span class="plain">if (ForActivity(PRINTING_A_PARSER_ERROR_ACT) == false) {</span>
<span class="plain">PARSER_ERROR_INTERNAL_RM('X', noun); new_line;</span>
<span class="plain">}</span>
<span class="plain">EndActivity(PRINTING_A_PARSER_ERROR_ACT);</span>
<span class="plain">@pull etype;</span>
<span class="plain">continue;</span>
<span class="plain">}</span>
<span class="plain">! Unless the opening word was OOPS, return</span>
<span class="plain">! Conveniently, a_table--&gt;1 is the first word on both the Z-machine and Glulx</span>
<span class="plain">w = a_table--&gt;1;</span>
<span class="plain">if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) {</span>
<span class="plain">if (oops_from == 0) { PARSER_COMMAND_INTERNAL_RM('A'); new_line; continue; }</span>
<span class="plain">if (nw == 1) { PARSER_COMMAND_INTERNAL_RM('B'); new_line; continue; }</span>
<span class="plain">if (nw &gt; 2) { PARSER_COMMAND_INTERNAL_RM('C'); new_line; continue; }</span>
<span class="plain">! So now we know: there was a previous mistake, and the player has</span>
<span class="plain">! attempted to correct a single word of it.</span>
<span class="plain">for (i=0 : i&lt;INPUT_BUFFER_LEN : i++) buffer2-&gt;i = a_buffer-&gt;i;</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">x1 = a_table-&gt;9; ! Start of word following "oops"</span>
<span class="plain">x2 = a_table-&gt;8; ! Length of word following "oops"</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">x1 = a_table--&gt;6; ! Start of word following "oops"</span>
<span class="plain">x2 = a_table--&gt;5; ! Length of word following "oops"</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">! Repair the buffer to the text that was in it before the "oops"</span>
<span class="plain">! was typed:</span>
<span class="plain">for (i=0 : i&lt;64 : i++) a_buffer-&gt;i = oops_workspace-&gt;i;</span>
<span class="plain">VM_Tokenise(a_buffer,a_table);</span>
<span class="plain">! Work out the position in the buffer of the word to be corrected:</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">w = a_table-&gt;(4*oops_from + 1); ! Start of word to go</span>
<span class="plain">w2 = a_table-&gt;(4*oops_from); ! Length of word to go</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">w = a_table--&gt;(3*oops_from); ! Start of word to go</span>
<span class="plain">w2 = a_table--&gt;(3*oops_from - 1); ! Length of word to go</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">! Write spaces over the word to be corrected:</span>
<span class="plain">for (i=0 : i&lt;w2 : i++) a_buffer-&gt;(i+w) = ' ';</span>
<span class="plain">if (w2 &lt; x2) {</span>
<span class="plain">! If the replacement is longer than the original, move up...</span>
<span class="plain">for (i=INPUT_BUFFER_LEN-1 : i&gt;=w+x2 : i--)</span>
<span class="plain">a_buffer-&gt;i = a_buffer-&gt;(i-x2+w2);</span>
<span class="plain">! ...increasing buffer size accordingly.</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">a_buffer-&gt;1 = (a_buffer-&gt;1) + (x2-w2);</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">a_buffer--&gt;0 = (a_buffer--&gt;0) + (x2-w2);</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">}</span>
<span class="plain">! Write the correction in:</span>
<span class="plain">for (i=0 : i&lt;x2 : i++) a_buffer-&gt;(i+w) = buffer2-&gt;(i+x1);</span>
<span class="plain">VM_Tokenise(a_buffer, a_table);</span>
<span class="plain">#Ifdef TARGET_ZCODE; nw = a_table-&gt;1; #Ifnot; nw = a_table--&gt;0; #Endif;</span>
<span class="plain">return nw;</span>
<span class="plain">}</span>
<span class="plain">! Undo handling</span>
<span class="plain">if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) &amp;&amp; (nw==1)) {</span>
<span class="plain">Perform_Undo();</span>
<span class="plain">continue;</span>
<span class="plain">}</span>
<span class="plain">i = VM_Save_Undo();</span>
<span class="plain">if (TEMPLATE_CONFIGURATION_BITMAP &amp; PREVENT_UNDO_TCBIT) undo_flag = 0;</span>
<span class="plain">else undo_flag = 2;</span>
<span class="plain">if (i == -1) undo_flag = 0;</span>
<span class="plain">if (i == 0) undo_flag = 1;</span>
<span class="plain">if (i == 2) {</span>
<span class="plain">VM_RestoreWindowColours();</span>
<span class="plain">VM_Style(SUBHEADER_VMSTY);</span>
<span class="plain">SL_Location(); print "^";</span>
<span class="plain">! print (name) location, "^";</span>
<span class="plain">VM_Style(NORMAL_VMSTY);</span>
<span class="plain">IMMEDIATELY_UNDO_RM('E'); new_line;</span>
<span class="plain">continue;</span>
<span class="plain">}</span>
<span class="plain">return nw;</span>
<span class="plain">}</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP9"></a><b>&#167;9. Parser Proper. </b>The main parser routine is something of a leviathan, and it has traditionally
been divided into 11 lettered parts:
</p>
<p class="inwebparagraph"></p>
<ul class="items"><li>(A) Get the input, do OOPS and AGAIN
</li><li>(B) Is it a direction, and so an implicit GO? If so go to (K)
</li><li>(C) Is anyone being addressed?
</li><li>(D) Get the command verb: try all the syntax lines for that verb
</li><li>(E) Break down a syntax line into analysed tokens
</li><li>(F) Look ahead for advance warning for <code class="display"><span class="extract">multiexcept</span></code>/<code class="display"><span class="extract">multiinside</span></code>
</li><li>(G) Parse each token in turn (calling <code class="display"><span class="extract">ParseToken</span></code> to do most of the work)
</li><li>(H) Cheaply parse otherwise unrecognised conversation and return
</li><li>(I) Print best possible error message
</li><li>(J) Retry the whole lot
</li><li>(K) Last thing: check for THEN and further instructions(s), return.
</li></ul>
<p class="inwebparagraph">This lettering has been preserved here, with the code under each letter
now being the body of "Parser Letter A", "Parser Letter B" and so on.
</p>
<p class="inwebparagraph">Note that there are three different places where a return can happen.
The routine returns only when a sensible request has been made; for a
fairly thorough description of its output, which is written into the
<code class="display"><span class="extract">parser_results</span></code> array and also into several globals (see "OrderOfPlay.i6t").
</p>
<pre class="display">
<span class="plain">[ Parser__parse</span>
<span class="plain">syntax line num_lines line_address i j k token l m inferred_go;</span>
<span class="plain">cobj_flag = 0;</span>
<span class="plain">parser_results--&gt;ACTION_PRES = 0;</span>
<span class="plain">parser_results--&gt;NO_INPS_PRES = 0;</span>
<span class="plain">parser_results--&gt;INP1_PRES = 0;</span>
<span class="plain">parser_results--&gt;INP2_PRES = 0;</span>
<span class="plain">meta = false;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP10"></a><b>&#167;10. Parser Letter A. </b>Get the input, do OOPS and AGAIN.
</p>
<pre class="display">
<span class="plain">if (held_back_mode) {</span>
<span class="plain">held_back_mode = false; wn = hb_wn;</span>
<span class="plain">if (verb_wordnum &gt; 0) i = WordAddress(verb_wordnum); else i = WordAddress(1);</span>
<span class="plain">j = WordAddress(wn);</span>
<span class="plain">if (i&lt;=j) for (: i&lt;j : i++) i-&gt;0 = ' ';</span>
<span class="plain">i = NextWord();</span>
<span class="plain">if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {</span>
<span class="plain">! Delete the words "then again" from the again buffer,</span>
<span class="plain">! in which we have just realised that it must occur:</span>
<span class="plain">! prevents an infinite loop on "i. again"</span>
<span class="plain">i = WordAddress(wn-2)-buffer;</span>
<span class="plain">if (wn &gt; num_words) j = INPUT_BUFFER_LEN-1;</span>
<span class="plain">else j = WordAddress(wn)-buffer;</span>
<span class="plain">for (: i&lt;j : i++) buffer3-&gt;i = ' ';</span>
<span class="plain">}</span>
<span class="plain">VM_Tokenise(buffer, parse);</span>
<span class="plain">jump ReParse;</span>
<span class="plain">}</span>
<span class="plain">.ReType;</span>
<span class="plain">cobj_flag = 0;</span>
<span class="plain">actors_location = ScopeCeiling(player);</span>
<span class="plain">BeginActivity(READING_A_COMMAND_ACT); if (ForActivity(READING_A_COMMAND_ACT)==false) {</span>
<span class="plain">Keyboard(buffer,parse);</span>
<span class="plain">num_words = WordCount(); players_command = 100 + num_words;</span>
<span class="plain">} if (EndActivity(READING_A_COMMAND_ACT)) jump ReType;</span>
<span class="plain">.ReParse;</span>
<span class="plain">parser_inflection = name;</span>
<span class="plain">! Initially assume the command is aimed at the player, and the verb</span>
<span class="plain">! is the first word</span>
<span class="plain">num_words = WordCount(); players_command = 100 + num_words;</span>
<span class="plain">wn = 1; inferred_go = false;</span>
<span class="plain">#Ifdef LanguageToInformese;</span>
<span class="plain">LanguageToInformese();</span>
<span class="plain">! Re-tokenise:</span>
<span class="plain">VM_Tokenise(buffer,parse);</span>
<span class="plain">#Endif; ! LanguageToInformese</span>
<span class="plain">num_words = WordCount(); players_command = 100 + num_words;</span>
<span class="plain">k=0;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 2) {</span>
<span class="plain">print "[ ";</span>
<span class="plain">for (i=0 : i&lt;num_words : i++) {</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">j = parse--&gt;(i*2 + 1);</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">j = parse--&gt;(i*3 + 1);</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">k = WordAddress(i+1);</span>
<span class="plain">l = WordLength(i+1);</span>
<span class="plain">print "~"; for (m=0 : m&lt;l : m++) print (char) k-&gt;m; print "~ ";</span>
<span class="plain">if (j == 0) print "?";</span>
<span class="plain">else {</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">if (UnsignedCompare(j, HDR_DICTIONARY--&gt;0) &gt;= 0 &amp;&amp;</span>
<span class="plain">UnsignedCompare(j, HDR_HIGHMEMORY--&gt;0) &lt; 0)</span>
<span class="plain">print (address) j;</span>
<span class="plain">else print j;</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">if (j-&gt;0 == $60) print (address) j;</span>
<span class="plain">else print j;</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">}</span>
<span class="plain">if (i ~= num_words-1) print " / ";</span>
<span class="plain">}</span>
<span class="plain">print " ]^";</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">verb_wordnum = 1;</span>
<span class="plain">actor = player;</span>
<span class="plain">actors_location = ScopeCeiling(player);</span>
<span class="plain">usual_grammar_after = 0;</span>
<span class="plain">.AlmostReParse;</span>
<span class="plain">scope_token = 0;</span>
<span class="plain">action_to_be = NULL;</span>
<span class="plain">! Begin from what we currently think is the verb word</span>
<span class="plain">.BeginCommand;</span>
<span class="plain">wn = verb_wordnum;</span>
<span class="plain">verb_word = NextWordStopped();</span>
<span class="plain">! If there's no input here, we must have something like "person,".</span>
<span class="plain">if (verb_word == -1) {</span>
<span class="plain">best_etype = STUCK_PE; jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">if (verb_word == comma_word) {</span>
<span class="plain">best_etype = COMMABEGIN_PE; jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">! Now try for "again" or "g", which are special cases: don't allow "again" if nothing</span>
<span class="plain">! has previously been typed; simply copy the previous text across</span>
<span class="plain">if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD;</span>
<span class="plain">if (verb_word == AGAIN1__WD) {</span>
<span class="plain">if (actor ~= player) {</span>
<span class="plain">best_etype = ANIMAAGAIN_PE;</span>
<span class="plain">jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">if (buffer3-&gt;1 == 0) {</span>
<span class="plain">PARSER_COMMAND_INTERNAL_RM('D'); new_line;</span>
<span class="plain">jump ReType;</span>
<span class="plain">}</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">if (buffer3--&gt;0 == 0) {</span>
<span class="plain">PARSER_COMMAND_INTERNAL_RM('D'); new_line;</span>
<span class="plain">jump ReType;</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">for (i=0 : i&lt;INPUT_BUFFER_LEN : i++) buffer-&gt;i = buffer3-&gt;i;</span>
<span class="plain">VM_Tokenise(buffer,parse);</span>
<span class="plain">num_words = WordCount(); players_command = 100 + num_words;</span>
<span class="plain">jump ReParse;</span>
<span class="plain">}</span>
<span class="plain">! Save the present input in case of an "again" next time</span>
<span class="plain">if (verb_word ~= AGAIN1__WD)</span>
<span class="plain">for (i=0 : i&lt;INPUT_BUFFER_LEN : i++) buffer3-&gt;i = buffer-&gt;i;</span>
<span class="plain">if (usual_grammar_after == 0) {</span>
<span class="plain">j = verb_wordnum;</span>
<span class="plain">i = RunRoutines(actor, grammar);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 2 &amp;&amp; actor.grammar ~= 0 or NULL)</span>
<span class="plain">print " [Grammar property returned ", i, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if ((i ~= 0 or 1) &amp;&amp; (VM_InvalidDictionaryAddress(i))) {</span>
<span class="plain">usual_grammar_after = verb_wordnum; i=-i;</span>
<span class="plain">}</span>
<span class="plain">if (i == 1) {</span>
<span class="plain">parser_results--&gt;ACTION_PRES = action;</span>
<span class="plain">parser_results--&gt;NO_INPS_PRES = 0;</span>
<span class="plain">parser_results--&gt;INP1_PRES = noun;</span>
<span class="plain">parser_results--&gt;INP2_PRES = second;</span>
<span class="plain">if (noun) parser_results--&gt;NO_INPS_PRES = 1;</span>
<span class="plain">if (second) parser_results--&gt;NO_INPS_PRES = 2;</span>
<span class="plain">rtrue;</span>
<span class="plain">}</span>
<span class="plain">if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; }</span>
<span class="plain">else { wn = verb_wordnum; verb_word = NextWord(); }</span>
<span class="plain">}</span>
<span class="plain">else usual_grammar_after = 0;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP11"></a><b>&#167;11. Parser Letter B. </b>Is the command a direction name, and so an implicit GO? If so, go to (K).
</p>
<pre class="display">
<span class="plain">if (verb_word == 0) {</span>
<span class="plain">i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);</span>
<span class="plain">wn = i;</span>
<span class="plain">}</span>
<span class="plain">! If the first word is not listed as a verb, it must be a direction</span>
<span class="plain">! or the name of someone to talk to</span>
<span class="plain">if (verb_word == 0 || ((verb_word-&gt;#dict_par1) &amp; 1) == 0) {</span>
<span class="plain">! So is the first word an object contained in the special object "compass"</span>
<span class="plain">! (i.e., a direction)? This needs use of NounDomain, a routine which</span>
<span class="plain">! does the object matching, returning the object number, or 0 if none found,</span>
<span class="plain">! or REPARSE_CODE if it has restructured the parse table so the whole parse</span>
<span class="plain">! must be begun again...</span>
<span class="plain">wn = verb_wordnum; indef_mode = false; token_filter = 0; parameters = 0;</span>
<span class="plain">@push actor; @push action; @push action_to_be;</span>
<span class="plain">actor = player; meta = false; action = ##Go; action_to_be = ##Go;</span>
<span class="plain">l = NounDomain(Compass, 0, 0);</span>
<span class="plain">@pull action_to_be; @pull action; @pull actor;</span>
<span class="plain">if (l == REPARSE_CODE) jump ReParse;</span>
<span class="plain">! If it is a direction, send back the results:</span>
<span class="plain">! action=GoSub, no of arguments=1, argument 1=the direction.</span>
<span class="plain">if ((l~=0) &amp;&amp; (l ofclass K3_direction)) {</span>
<span class="plain">parser_results--&gt;ACTION_PRES = ##Go;</span>
<span class="plain">parser_results--&gt;NO_INPS_PRES = 1;</span>
<span class="plain">parser_results--&gt;INP1_PRES = l;</span>
<span class="plain">inferred_go = true;</span>
<span class="plain">jump LookForMore;</span>
<span class="plain">}</span>
<span class="plain">} ! end of first-word-not-a-verb</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP12"></a><b>&#167;12. Parser Letter C. </b>Is anyone being addressed?
</p>
<pre class="display">
<span class="plain">! Only check for a comma (a "someone, do something" command) if we are</span>
<span class="plain">! not already in the middle of one. (This simplification stops us from</span>
<span class="plain">! worrying about "robot, wizard, you are an idiot", telling the robot to</span>
<span class="plain">! tell the wizard that she is an idiot.)</span>
<span class="plain">if (actor == player) {</span>
<span class="plain">for (j=2 : j&lt;=num_words : j++) {</span>
<span class="plain">i=NextWord();</span>
<span class="plain">if (i == comma_word) jump Conversation;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">jump NotConversation;</span>
<span class="plain">! NextWord nudges the word number wn on by one each time, so we've now</span>
<span class="plain">! advanced past a comma. (A comma is a word all on its own in the table.)</span>
<span class="plain">.Conversation;</span>
<span class="plain">j = wn - 1;</span>
<span class="plain">! Use NounDomain (in the context of "animate creature") to see if the</span>
<span class="plain">! words make sense as the name of someone held or nearby</span>
<span class="plain">wn = 1; lookahead = HELD_TOKEN;</span>
<span class="plain">scope_reason = TALKING_REASON;</span>
<span class="plain">l = NounDomain(player,actors_location,6);</span>
<span class="plain">scope_reason = PARSING_REASON;</span>
<span class="plain">if (l == REPARSE_CODE) jump ReParse;</span>
<span class="plain">if (l == 0) {</span>
<span class="plain">if (verb_word &amp;&amp; ((verb_word-&gt;#dict_par1) &amp; 1)) jump NotConversation;</span>
<span class="plain">best_etype = MISSINGPERSON_PE; jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">.Conversation2;</span>
<span class="plain">! The object addressed must at least be "talkable" if not actually "animate"</span>
<span class="plain">! (the distinction allows, for instance, a microphone to be spoken to,</span>
<span class="plain">! without the parser thinking that the microphone is human).</span>
<span class="plain">if (l hasnt animate &amp;&amp; l hasnt talkable) {</span>
<span class="plain">best_etype = ANIMALISTEN_PE; noun = l; jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">! Check that there aren't any mystery words between the end of the person's</span>
<span class="plain">! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").</span>
<span class="plain">if (wn ~= j) {</span>
<span class="plain">if (verb_word &amp;&amp; ((verb_word-&gt;#dict_par1) &amp; 1)) jump NotConversation;</span>
<span class="plain">best_etype = TOTALK_PE; jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">! The player has now successfully named someone. Adjust "him", "her", "it":</span>
<span class="plain">PronounNotice(l);</span>
<span class="plain">! Set the global variable "actor", adjust the number of the first word,</span>
<span class="plain">! and begin parsing again from there.</span>
<span class="plain">verb_wordnum = j + 1;</span>
<span class="plain">! Stop things like "me, again":</span>
<span class="plain">if (l == player) {</span>
<span class="plain">wn = verb_wordnum;</span>
<span class="plain">if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {</span>
<span class="plain">best_etype = ANIMAAGAIN_PE;</span>
<span class="plain">jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">actor = l;</span>
<span class="plain">actors_location = ScopeCeiling(l);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 1)</span>
<span class="plain">print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">jump BeginCommand;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP13"></a><b>&#167;13. Parser Letter D. </b>Get the verb: try all the syntax lines for that verb.
</p>
<pre class="display">
<span class="plain">.NotConversation;</span>
<span class="plain">if (verb_word == 0 || ((verb_word-&gt;#dict_par1) &amp; 1) == 0) {</span>
<span class="plain">verb_word = UnknownVerb(verb_word);</span>
<span class="plain">if (verb_word ~= 0) jump VerbAccepted;</span>
<span class="plain">best_etype = VERB_PE;</span>
<span class="plain">jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">.VerbAccepted;</span>
<span class="plain">! We now definitely have a verb, not a direction, whether we got here by the</span>
<span class="plain">! "take ..." or "person, take ..." method. Get the meta flag for this verb:</span>
<span class="plain">meta = ((verb_word-&gt;#dict_par1) &amp; 2)/2;</span>
<span class="plain">! You can't order other people to "full score" for you, and so on...</span>
<span class="plain">if (meta == 1 &amp;&amp; actor ~= player) {</span>
<span class="plain">best_etype = VERB_PE;</span>
<span class="plain">meta = 0;</span>
<span class="plain">jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">! Now let i be the corresponding verb number...</span>
<span class="plain">i = DictionaryWordToVerbNum(verb_word);</span>
<span class="plain">! ...then look up the i-th entry in the verb table, whose address is at word</span>
<span class="plain">! 7 in the Z-machine (in the header), so as to get the address of the syntax</span>
<span class="plain">! table for the given verb...</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">syntax = (HDR_STATICMEMORY--&gt;0)--&gt;i;</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">syntax = (#grammar_table)--&gt;(i+1);</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">! ...and then see how many lines (ie, different patterns corresponding to the</span>
<span class="plain">! same verb) are stored in the parse table...</span>
<span class="plain">num_lines = (syntax-&gt;0) - 1;</span>
<span class="plain">! ...and now go through them all, one by one.</span>
<span class="plain">! To prevent pronoun_word 0 being misunderstood,</span>
<span class="plain">pronoun_word = NULL; pronoun_obj = NULL;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 1)</span>
<span class="plain">print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">best_etype = STUCK_PE; nextbest_etype = STUCK_PE;</span>
<span class="plain">multiflag = false;</span>
<span class="plain">! "best_etype" is the current failure-to-match error - it is by default</span>
<span class="plain">! the least informative one, "don't understand that sentence".</span>
<span class="plain">! "nextbest_etype" remembers the best alternative to having to ask a</span>
<span class="plain">! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).</span>
<span class="plain">! multiflag is used here to prevent inappropriate MULTI_PE errors</span>
<span class="plain">! in addition to its unrelated duties passing information to action routines</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP14"></a><b>&#167;14. Parser Letter E. </b>Break down a syntax line into analysed tokens.
</p>
<pre class="display">
<span class="plain">line_address = syntax + 1;</span>
<span class="plain">for (line=0 : line&lt;=num_lines : line++) {</span>
<span class="plain">! Unpack the syntax line from Inform format into three arrays; ensure that</span>
<span class="plain">! the sequence of tokens ends in an ENDIT_TOKEN.</span>
<span class="plain">line_address = UnpackGrammarLine(line_address);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 1) {</span>
<span class="plain">if (parser_trace &gt;= 2) new_line;</span>
<span class="plain">print "[line ", line; DebugGrammarLine();</span>
<span class="plain">print "]^";</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">! We aren't in "not holding" or inferring modes, and haven't entered</span>
<span class="plain">! any parameters on the line yet, or any special numbers; the multiple</span>
<span class="plain">! object is still empty.</span>
<span class="plain">inferfrom = 0;</span>
<span class="plain">parameters = 0;</span>
<span class="plain">nsns = 0; special_word = 0;</span>
<span class="plain">multiple_object--&gt;0 = 0;</span>
<span class="plain">multi_context = 0;</span>
<span class="plain">etype = STUCK_PE;</span>
<span class="plain">! Put the word marker back to just after the verb</span>
<span class="plain">wn = verb_wordnum+1;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP15"></a><b>&#167;15. Parser Letter F. </b>Look ahead for advance warning for <code class="display"><span class="extract">multiexcept</span></code>/<code class="display"><span class="extract">multiinside</span></code>.
</p>
<p class="inwebparagraph">There are two special cases where parsing a token now has to be affected by
the result of parsing another token later, and these two cases (multiexcept
and multiinside tokens) are helped by a quick look ahead, to work out the
future token now. We can only carry this out in the simple (but by far the
most common) case:
</p>
<p class="inwebparagraph"></p>
<pre class="display">
<span class="plain">multiexcept &lt;one or more prepositions&gt; noun</span>
</pre>
<p class="inwebparagraph">and similarly for <code class="display"><span class="extract">multiinside</span></code>.
</p>
<pre class="display">
<span class="plain">advance_warning = -1; indef_mode = false;</span>
<span class="plain">for (i=0,m=false,pcount=0 : line_token--&gt;pcount ~= ENDIT_TOKEN : pcount++) {</span>
<span class="plain">scope_token = 0;</span>
<span class="plain">if (line_ttype--&gt;pcount ~= PREPOSITION_TT) i++;</span>
<span class="plain">if (line_ttype--&gt;pcount == ELEMENTARY_TT) {</span>
<span class="plain">if (line_tdata--&gt;pcount == MULTI_TOKEN) m = true;</span>
<span class="plain">if (line_tdata--&gt;pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN &amp;&amp; i == 1) {</span>
<span class="plain">! First non-preposition is "multiexcept" or</span>
<span class="plain">! "multiinside", so look ahead.</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 2) print " [Trying look-ahead]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">! We need this to be followed by 1 or more prepositions.</span>
<span class="plain">pcount++;</span>
<span class="plain">if (line_ttype--&gt;pcount == PREPOSITION_TT) {</span>
<span class="plain">! skip ahead to a preposition word in the input</span>
<span class="plain">do {</span>
<span class="plain">l = NextWord();</span>
<span class="plain">} until ((wn &gt; num_words) ||</span>
<span class="plain">(l &amp;&amp; (l-&gt;#dict_par1) &amp; 8 ~= 0));</span>
<span class="plain">if (wn &gt; num_words) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 2)</span>
<span class="plain">print " [Look-ahead aborted: prepositions missing]^";</span>
<span class="plain">#Endif;</span>
<span class="plain">jump EmptyLine;</span>
<span class="plain">}</span>
<span class="plain">do {</span>
<span class="plain">if (PrepositionChain(l, pcount) ~= -1) {</span>
<span class="plain">! advance past the chain</span>
<span class="plain">if ((line_token--&gt;pcount)-&gt;0 &amp; $20 ~= 0) {</span>
<span class="plain">pcount++;</span>
<span class="plain">while ((line_token--&gt;pcount ~= ENDIT_TOKEN) &amp;&amp;</span>
<span class="plain">((line_token--&gt;pcount)-&gt;0 &amp; $10 ~= 0))</span>
<span class="plain">pcount++;</span>
<span class="plain">} else {</span>
<span class="plain">pcount++;</span>
<span class="plain">}</span>
<span class="plain">} else {</span>
<span class="plain">! try to find another preposition word</span>
<span class="plain">do {</span>
<span class="plain">l = NextWord();</span>
<span class="plain">} until ((wn &gt;= num_words) ||</span>
<span class="plain">(l &amp;&amp; (l-&gt;#dict_par1) &amp; 8 ~= 0));</span>
<span class="plain">if (l &amp;&amp; (l-&gt;#dict_par1) &amp; 8) continue;</span>
<span class="plain">! lookahead failed</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 2)</span>
<span class="plain">print " [Look-ahead aborted: prepositions don't match]^";</span>
<span class="plain">#endif;</span>
<span class="plain">jump LineFailed;</span>
<span class="plain">}</span>
<span class="plain">if (wn &lt;= num_words) l = NextWord();</span>
<span class="plain">} until (line_ttype--&gt;pcount ~= PREPOSITION_TT);</span>
<span class="plain">.EmptyLine;</span>
<span class="plain">! put back the non-preposition we just read</span>
<span class="plain">wn--;</span>
<span class="plain">if ((line_ttype--&gt;pcount == ELEMENTARY_TT) &amp;&amp;</span>
<span class="plain">(line_tdata--&gt;pcount == NOUN_TOKEN)) {</span>
<span class="plain">l = Descriptors(); ! skip past THE etc</span>
<span class="plain">if (l~=0) etype=l; ! don't allow multiple objects</span>
<span class="plain">k = parser_results--&gt;INP1_PRES; @push k; @push parameters;</span>
<span class="plain">parameters = 1; parser_results--&gt;INP1_PRES = 0;</span>
<span class="plain">l = NounDomain(actors_location, actor, NOUN_TOKEN, true);</span>
<span class="plain">@pull parameters; @pull k; parser_results--&gt;INP1_PRES = k;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 2) {</span>
<span class="plain">print " [Advanced to ~noun~ token: ";</span>
<span class="plain">if (l == REPARSE_CODE) print "re-parse request]^";</span>
<span class="plain">else {</span>
<span class="plain">if (l == 1) print "but multiple found]^";</span>
<span class="plain">if (l == 0) print "error ", etype, "]^";</span>
<span class="plain">if (l &gt;= 2) print (the) l, "]^";</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (l == REPARSE_CODE) jump ReParse;</span>
<span class="plain">if (l &gt;= 2) advance_warning = l;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">break;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! Slightly different line-parsing rules will apply to "take multi", to</span>
<span class="plain">! prevent "take all" behaving correctly but misleadingly when there's</span>
<span class="plain">! nothing to take.</span>
<span class="plain">take_all_rule = 0;</span>
<span class="plain">if (m &amp;&amp; params_wanted == 1 &amp;&amp; action_to_be == ##Take)</span>
<span class="plain">take_all_rule = 1;</span>
<span class="plain">! And now start again, properly, forearmed or not as the case may be.</span>
<span class="plain">! As a precaution, we clear all the variables again (they may have been</span>
<span class="plain">! disturbed by the call to NounDomain, which may have called outside</span>
<span class="plain">! code, which may have done anything!).</span>
<span class="plain">inferfrom = 0;</span>
<span class="plain">parameters = 0;</span>
<span class="plain">nsns = 0; special_word = 0;</span>
<span class="plain">multiple_object--&gt;0 = 0;</span>
<span class="plain">etype = STUCK_PE;</span>
<span class="plain">wn = verb_wordnum+1;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP16"></a><b>&#167;16. Parser Letter G. </b>Parse each token in turn (calling <code class="display"><span class="extract">ParseToken</span></code> to do most of the work).
</p>
<p class="inwebparagraph">The <code class="display"><span class="extract">pattern</span></code> gradually accumulates what has been recognised so far,
so that it may be reprinted by the parser later on.
</p>
<pre class="display">
<span class="plain">m = true;</span>
<span class="plain">for (pcount=1 : : pcount++)</span>
<span class="plain">if (line_token--&gt;(pcount-1) == ENDIT_TOKEN) {</span>
<span class="plain">if (pcount &gt;= 2) {</span>
<span class="plain">while ((((line_token--&gt;(pcount-2))-&gt;0) &amp; $10) ~= 0) pcount--;</span>
<span class="plain">AnalyseToken(line_token--&gt;(pcount-2));</span>
<span class="plain">if (found_ttype == PREPOSITION_TT) {</span>
<span class="plain">l = -1;</span>
<span class="plain">while (true) {</span>
<span class="plain">m = NextWordStopped();</span>
<span class="plain">if (m == -1) break;</span>
<span class="plain">l = m;</span>
<span class="plain">}</span>
<span class="plain">if (PrepositionChain(l, pcount-2) == -1) {</span>
<span class="plain">m = false;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 2)</span>
<span class="plain">print "[line rejected for not ending with correct preposition]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">} else m = true;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">break;</span>
<span class="plain">}</span>
<span class="plain">wn = verb_wordnum+1;</span>
<span class="plain">if (m) for (pcount=1 : : pcount++) {</span>
<span class="plain">pattern--&gt;pcount = PATTERN_NULL; scope_token = 0;</span>
<span class="plain">token = line_token--&gt;(pcount-1);</span>
<span class="plain">lookahead = line_token--&gt;pcount;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 2)</span>
<span class="plain">print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token,</span>
<span class="plain">"]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (token ~= ENDIT_TOKEN) {</span>
<span class="plain">scope_reason = PARSING_REASON;</span>
<span class="plain">AnalyseToken(token);</span>
<span class="plain">l = ParseToken(found_ttype, found_tdata, pcount-1, token);</span>
<span class="plain">while ((l &gt;= GPR_NOUN) &amp;&amp; (l &lt; -1)) l = ParseToken(ELEMENTARY_TT, l + 256);</span>
<span class="plain">scope_reason = PARSING_REASON;</span>
<span class="plain">if (l == GPR_PREPOSITION) {</span>
<span class="plain">if (found_ttype~=PREPOSITION_TT &amp;&amp; (found_ttype~=ELEMENTARY_TT ||</span>
<span class="plain">found_tdata~=TOPIC_TOKEN)) params_wanted--;</span>
<span class="plain">l = true;</span>
<span class="plain">}</span>
<span class="plain">else</span>
<span class="plain">if (l &lt; 0) l = false;</span>
<span class="plain">else</span>
<span class="plain">if (l ~= GPR_REPARSE) {</span>
<span class="plain">if (l == GPR_NUMBER) {</span>
<span class="plain">if (nsns == 0) special_number1 = parsed_number;</span>
<span class="plain">else special_number2 = parsed_number;</span>
<span class="plain">nsns++; l = 1;</span>
<span class="plain">}</span>
<span class="plain">if (l == GPR_MULTIPLE) l = 0;</span>
<span class="plain">parser_results--&gt;(parameters+INP1_PRES) = l;</span>
<span class="plain">parameters++;</span>
<span class="plain">pattern--&gt;pcount = l;</span>
<span class="plain">l = true;</span>
<span class="plain">}</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) {</span>
<span class="plain">print " [token resulted in ";</span>
<span class="plain">if (l == REPARSE_CODE) print "re-parse request]^";</span>
<span class="plain">if (l == 0) print "failure with error type ", etype, "]^";</span>
<span class="plain">if (l == 1) print "success]^";</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (l == REPARSE_CODE) jump ReParse;</span>
<span class="plain">if (l == false) break;</span>
<span class="plain">}</span>
<span class="plain">else {</span>
<span class="plain">! If the player has entered enough already but there's still</span>
<span class="plain">! text to wade through: store the pattern away so as to be able to produce</span>
<span class="plain">! a decent error message if this turns out to be the best we ever manage,</span>
<span class="plain">! and in the mean time give up on this line</span>
<span class="plain">! However, if the superfluous text begins with a comma or "then" then</span>
<span class="plain">! take that to be the start of another instruction</span>
<span class="plain">if (wn &lt;= num_words) {</span>
<span class="plain">l = NextWord();</span>
<span class="plain">if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {</span>
<span class="plain">held_back_mode = true; hb_wn = wn-1;</span>
<span class="plain">} else {</span>
<span class="plain">for (m=0 : m&lt;32 : m++) pattern2--&gt;m = pattern--&gt;m;</span>
<span class="plain">pcount2 = pcount;</span>
<span class="plain">etype = UPTO_PE;</span>
<span class="plain">break;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! Now, we may need to revise the multiple object because of the single one</span>
<span class="plain">! we now know (but didn't when the list was drawn up).</span>
<span class="plain">if (parameters &gt;= 1) {</span>
<span class="plain">if (parser_results--&gt;INP1_PRES == 0) {</span>
<span class="plain">l = ReviseMulti(parser_results--&gt;INP2_PRES);</span>
<span class="plain">if (l ~= 0) { etype = l; parser_results--&gt;ACTION_PRES = action_to_be; break; }</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (parameters &gt;= 2) {</span>
<span class="plain">if (parser_results--&gt;INP2_PRES == 0) {</span>
<span class="plain">l = ReviseMulti(parser_results--&gt;INP1_PRES);</span>
<span class="plain">if (l ~= 0) { etype = l; break; }</span>
<span class="plain">} else {</span>
<span class="plain">k = parser_results--&gt;INP1_PRES; l = parser_results--&gt;INP2_PRES;</span>
<span class="plain">if (k &amp;&amp; l) {</span>
<span class="plain">if ((multi_context==MULTIEXCEPT_TOKEN &amp;&amp; k == l) ||</span>
<span class="plain">((multi_context==MULTIINSIDE_TOKEN &amp;&amp; k notin l &amp;&amp; l notin k))) {</span>
<span class="plain">best_etype = NOTHING_PE;</span>
<span class="plain">parser_results--&gt;ACTION_PRES = action_to_be; jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! To trap the case of "take all" inferring only "yourself" when absolutely</span>
<span class="plain">! nothing else is in the vicinity...</span>
<span class="plain">if (take_all_rule == 2 &amp;&amp; parser_results--&gt;INP1_PRES == actor) {</span>
<span class="plain">best_etype = NOTHING_PE;</span>
<span class="plain">jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 1) print "[Line successfully parsed]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">! The line has successfully matched the text. Declare the input error-free...</span>
<span class="plain">oops_from = 0;</span>
<span class="plain">! ...explain any inferences made (using the pattern)...</span>
<span class="plain">if (inferfrom ~= 0) {</span>
<span class="plain">PrintInferredCommand(inferfrom);</span>
<span class="plain">ClearParagraphing(20);</span>
<span class="plain">}</span>
<span class="plain">! ...copy the action number, and the number of parameters...</span>
<span class="plain">parser_results--&gt;ACTION_PRES = action_to_be;</span>
<span class="plain">parser_results--&gt;NO_INPS_PRES = parameters;</span>
<span class="plain">! ...reverse first and second parameters if need be...</span>
<span class="plain">if (action_reversed &amp;&amp; parameters == 2) {</span>
<span class="plain">i = parser_results--&gt;INP1_PRES;</span>
<span class="plain">parser_results--&gt;INP1_PRES = parser_results--&gt;INP2_PRES;</span>
<span class="plain">parser_results--&gt;INP2_PRES = i;</span>
<span class="plain">if (nsns == 2) {</span>
<span class="plain">i = special_number1; special_number1 = special_number2;</span>
<span class="plain">special_number2 = i;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! ...and to reset "it"-style objects to the first of these parameters, if</span>
<span class="plain">! there is one (and it really is an object)...</span>
<span class="plain">if (parameters &gt; 0 &amp;&amp; parser_results--&gt;INP1_PRES &gt;= 2)</span>
<span class="plain">PronounNotice(parser_results--&gt;INP1_PRES);</span>
<span class="plain">! ...and return from the parser altogether, having successfully matched</span>
<span class="plain">! a line.</span>
<span class="plain">if (held_back_mode) {</span>
<span class="plain">wn=hb_wn;</span>
<span class="plain">jump LookForMore;</span>
<span class="plain">}</span>
<span class="plain">rtrue;</span>
<span class="plain">} ! end of if(token ~= ENDIT_TOKEN) else</span>
<span class="plain">} ! end of for(pcount++)</span>
<span class="plain">.LineFailed;</span>
<span class="plain">! The line has failed to match.</span>
<span class="plain">! We continue the outer "for" loop, trying the next line in the grammar.</span>
<span class="plain">if (etype &gt; best_etype) best_etype = etype;</span>
<span class="plain">if (etype ~= ASKSCOPE_PE &amp;&amp; etype &gt; nextbest_etype) nextbest_etype = etype;</span>
<span class="plain">! ...unless the line was something like "take all" which failed because</span>
<span class="plain">! nothing matched the "all", in which case we stop and give an error now.</span>
<span class="plain">if (take_all_rule == 2 &amp;&amp; etype==NOTHING_PE) break;</span>
<span class="plain">} ! end of for(line++)</span>
<span class="plain">! The grammar is exhausted: every line has failed to match.</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP17"></a><b>&#167;17. Parser Letter H. </b>Cheaply parse otherwise unrecognised conversation and return.
</p>
<p class="inwebparagraph">(Errors are handled differently depending on who was talking. If the command
was addressed to somebody else (eg, DWARF, SFGH) then it is taken as
conversation which the parser has no business in disallowing.)
</p>
<p class="inwebparagraph">The parser used to return the fake action <code class="display"><span class="extract">##NotUnderstood</span></code> when a
command in the form PERSON, ARFLE BARFLE GLOOP is parsed, where a character
is addressed but with an instruction which the parser can't understand.
(If a command such as ARFLE BARFLE GLOOP is not an instruction to someone
else, the parser prints an error and requires the player to type another
command: thus <code class="display"><span class="extract">##NotUnderstood</span></code> was only returned when <code class="display"><span class="extract">actor</span></code> is not the
player.) And I6 had elaborate object-oriented ways to deal with this, but we
won't use any of that: we simply convert to a <code class="display"><span class="extract">##Answer</span></code> action, which
communicates a snippet of words to another character, just as if the
player had typed ANSWER ARFLE BARFLE GLOOP TO PERSON. For I7 purposes, the
fake action <code class="display"><span class="extract">##NotUnderstood</span></code> does not exist.
</p>
<pre class="display">
<span class="plain">.GiveError;</span>
<span class="plain">etype = best_etype;</span>
<span class="plain">if (actor ~= player) {</span>
<span class="plain">if (usual_grammar_after ~= 0) {</span>
<span class="plain">verb_wordnum = usual_grammar_after;</span>
<span class="plain">jump AlmostReParse;</span>
<span class="plain">}</span>
<span class="plain">wn = verb_wordnum;</span>
<span class="plain">special_word = NextWord();</span>
<span class="plain">if (special_word == comma_word) {</span>
<span class="plain">special_word = NextWord();</span>
<span class="plain">verb_wordnum++;</span>
<span class="plain">}</span>
<span class="plain">parser_results--&gt;ACTION_PRES = ##Answer;</span>
<span class="plain">parser_results--&gt;NO_INPS_PRES = 2;</span>
<span class="plain">parser_results--&gt;INP1_PRES = actor;</span>
<span class="plain">parser_results--&gt;INP2_PRES = 1; special_number1 = special_word;</span>
<span class="plain">actor = player;</span>
<span class="plain">consult_from = verb_wordnum; consult_words = num_words-consult_from+1;</span>
<span class="plain">rtrue;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP18"></a><b>&#167;18. Parser Letter I. </b>Print best possible error message.
</p>
<pre class="display">
<span class="plain">! If the player was the actor (eg, in "take dfghh") the error must be printed,</span>
<span class="plain">! and fresh input called for. In three cases the oops word must be jiggled.</span>
<span class="plain">if ((etype ofclass Routine) || (etype ofclass String)) {</span>
<span class="plain">if (ParserError(etype) ~= 0) jump ReType;</span>
<span class="plain">} else {</span>
<span class="plain">if (verb_wordnum == 0 &amp;&amp; etype == CANTSEE_PE) etype = VERB_PE;</span>
<span class="plain">players_command = 100 + WordCount(); ! The snippet variable "player's command"</span>
<span class="plain">BeginActivity(PRINTING_A_PARSER_ERROR_ACT);</span>
<span class="plain">if (ForActivity(PRINTING_A_PARSER_ERROR_ACT)) jump SkipParserError;</span>
<span class="plain">}</span>
<span class="plain">pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;</span>
<span class="plain">if (etype == STUCK_PE) { PARSER_ERROR_INTERNAL_RM('A'); new_line; oops_from = 1; }</span>
<span class="plain">if (etype == UPTO_PE) {</span>
<span class="plain">if (inferred_go) PARSER_ERROR_INTERNAL_RM('C');</span>
<span class="plain">else PARSER_ERROR_INTERNAL_RM('B');</span>
<span class="plain">for (m=0 : m&lt;32 : m++) pattern--&gt;m = pattern2--&gt;m;</span>
<span class="plain">pcount = pcount2; PrintCommand(0);</span>
<span class="plain">print ".^";</span>
<span class="plain">}</span>
<span class="plain">if (etype == NUMBER_PE) { PARSER_ERROR_INTERNAL_RM('D'); new_line; }</span>
<span class="plain">if (etype == CANTSEE_PE) { PARSER_ERROR_INTERNAL_RM('E'); new_line; oops_from=saved_oops; }</span>
<span class="plain">if (etype == TOOLIT_PE) { PARSER_ERROR_INTERNAL_RM('F'); new_line; }</span>
<span class="plain">if (etype == NOTHELD_PE) { PARSER_ERROR_INTERNAL_RM('G'); new_line; oops_from=saved_oops; }</span>
<span class="plain">if (etype == MULTI_PE) { PARSER_ERROR_INTERNAL_RM('H'); new_line; }</span>
<span class="plain">if (etype == MMULTI_PE) { PARSER_ERROR_INTERNAL_RM('I'); new_line; }</span>
<span class="plain">if (etype == VAGUE_PE) { PARSER_ERROR_INTERNAL_RM('J'); new_line; }</span>
<span class="plain">if (etype == ITGONE_PE) {</span>
<span class="plain">if (pronoun_obj == NULL) { PARSER_ERROR_INTERNAL_RM('J'); new_line; }</span>
<span class="plain">else { PARSER_ERROR_INTERNAL_RM('K', noun); new_line; }</span>
<span class="plain">}</span>
<span class="plain">if (etype == EXCEPT_PE) { PARSER_ERROR_INTERNAL_RM('L'); new_line; }</span>
<span class="plain">if (etype == ANIMA_PE) { PARSER_ERROR_INTERNAL_RM('M'); new_line; }</span>
<span class="plain">if (etype == VERB_PE) { PARSER_ERROR_INTERNAL_RM('N'); new_line; }</span>
<span class="plain">if (etype == SCENERY_PE) { PARSER_ERROR_INTERNAL_RM('O'); new_line; }</span>
<span class="plain">if (etype == JUNKAFTER_PE) { PARSER_ERROR_INTERNAL_RM('P'); new_line; }</span>
<span class="plain">if (etype == TOOFEW_PE) { PARSER_ERROR_INTERNAL_RM('Q', multi_had); new_line; }</span>
<span class="plain">if (etype == NOTHING_PE) {</span>
<span class="plain">if (parser_results--&gt;ACTION_PRES == ##Remove &amp;&amp;</span>
<span class="plain">parser_results--&gt;INP2_PRES ofclass Object) {</span>
<span class="plain">noun = parser_results--&gt;INP2_PRES; ! ensure valid for messages</span>
<span class="plain">if (noun has animate) { PARSER_N_ERROR_INTERNAL_RM('C', noun); new_line; }</span>
<span class="plain">else if (noun hasnt container or supporter) { PARSER_N_ERROR_INTERNAL_RM('D', noun); new_line; }</span>
<span class="plain">else if (noun has container &amp;&amp; noun hasnt open) { PARSER_N_ERROR_INTERNAL_RM('E', noun); new_line; }</span>
<span class="plain">else if (children(noun)==0) { PARSER_N_ERROR_INTERNAL_RM('F', noun); new_line; }</span>
<span class="plain">else parser_results--&gt;ACTION_PRES = 0;</span>
<span class="plain">}</span>
<span class="plain">if (parser_results--&gt;ACTION_PRES ~= ##Remove) {</span>
<span class="plain">if (multi_wanted==100) { PARSER_N_ERROR_INTERNAL_RM('A'); new_line; }</span>
<span class="plain">else { PARSER_N_ERROR_INTERNAL_RM('B'); new_line; }</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (etype == NOTINCONTEXT_PE) { PARSER_ERROR_INTERNAL_RM('R'); new_line; }</span>
<span class="plain">if (etype == ANIMAAGAIN_PE) { PARSER_ERROR_INTERNAL_RM('S'); new_line; }</span>
<span class="plain">if (etype == COMMABEGIN_PE) { PARSER_ERROR_INTERNAL_RM('T'); new_line; }</span>
<span class="plain">if (etype == MISSINGPERSON_PE) { PARSER_ERROR_INTERNAL_RM('U'); new_line; }</span>
<span class="plain">if (etype == ANIMALISTEN_PE) { PARSER_ERROR_INTERNAL_RM('V', noun); new_line; }</span>
<span class="plain">if (etype == TOTALK_PE) { PARSER_ERROR_INTERNAL_RM('W'); new_line; }</span>
<span class="plain">if (etype == ASKSCOPE_PE) {</span>
<span class="plain">scope_stage = 3;</span>
<span class="plain">if (indirect(scope_error) == -1) {</span>
<span class="plain">best_etype = nextbest_etype;</span>
<span class="plain">if (~~((etype ofclass Routine) || (etype ofclass String)))</span>
<span class="plain">EndActivity(PRINTING_A_PARSER_ERROR_ACT);</span>
<span class="plain">jump GiveError;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">.SkipParserError;</span>
<span class="plain">if ((etype ofclass Routine) || (etype ofclass String)) jump ReType;</span>
<span class="plain">say__p = 1;</span>
<span class="plain">EndActivity(PRINTING_A_PARSER_ERROR_ACT);</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP19"></a><b>&#167;19. Parser Letter J. </b>Retry the whole lot.
</p>
<pre class="display">
<span class="plain">! And go (almost) right back to square one...</span>
<span class="plain">jump ReType;</span>
<span class="plain">! ...being careful not to go all the way back, to avoid infinite repetition</span>
<span class="plain">! of a deferred command causing an error.</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP20"></a><b>&#167;20. Parser Letter K. </b>Last thing: check for THEN and further instructions(s), return.
</p>
<pre class="display">
<span class="plain">! At this point, the return value is all prepared, and we are only looking</span>
<span class="plain">! to see if there is a "then" followed by subsequent instruction(s).</span>
<span class="plain">.LookForMore;</span>
<span class="plain">if (wn &gt; num_words) rtrue;</span>
<span class="plain">i = NextWord();</span>
<span class="plain">if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {</span>
<span class="plain">if (wn &gt; num_words) {</span>
<span class="plain">held_back_mode = false;</span>
<span class="plain">return;</span>
<span class="plain">}</span>
<span class="plain">hb_wn = wn;</span>
<span class="plain">held_back_mode = true;</span>
<span class="plain">return;</span>
<span class="plain">}</span>
<span class="plain">best_etype = UPTO_PE;</span>
<span class="plain">jump GiveError;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP21"></a><b>&#167;21. End of Parser Proper. </b></p>
<pre class="display">
<span class="plain">]; ! end of Parser__parse</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP22"></a><b>&#167;22. Internal Rule. </b>As a hook on which to hang responses.
</p>
<pre class="display">
<span class="plain">[ PARSER_ERROR_INTERNAL_R; ];</span>
<span class="plain">[ PARSER_N_ERROR_INTERNAL_R; ];</span>
<span class="plain">[ PARSER_COMMAND_INTERNAL_R; ];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP23"></a><b>&#167;23. Parse Token. </b>The main parsing routine above tried a sequence of "grammar lines" in turn,
matching each against the text typed until one fitted. A grammar line is
itself a sequence of "grammar tokens". Here we have to parse the tokens.
</p>
<p class="inwebparagraph"><code class="display"><span class="extract">ParseToken(type, data)</span></code> tries the match text beginning at the current
word marker <code class="display"><span class="extract">wn</span></code> against a token of the given <code class="display"><span class="extract">type</span></code>, with the given <code class="display"><span class="extract">data</span></code>.
The optional further arguments <code class="display"><span class="extract">token_n</span></code> and <code class="display"><span class="extract">token</span></code> supply the token
number in the current grammar line (because some tokens do depend on what
has happened before or is needed later) and the address of the dictionary
word which makes up the <code class="display"><span class="extract">token</span></code>, in the case where it's a "preposition".
</p>
<p class="inwebparagraph">The return values are:
</p>
<ul class="items"><li>(a) <code class="display"><span class="extract">GPR_REPARSE</span></code> for "I have rewritten the command, please re-parse from scratch";
</li><li>(b) <code class="display"><span class="extract">GPR_PREPOSITION</span></code> for "token accepted with no result";
</li><li>(c) -256 + x for "please parse <code class="display"><span class="extract">ParseToken(ELEMENTARY_TT, x)</span></code> instead";
</li><li>(d) 0 for "token accepted, result is the multiple object list";
</li><li>(e) 1 for "token accepted, result is the number in <code class="display"><span class="extract">parsed_number</span></code>";
</li><li>(f) an object number for "token accepted with this object as result";
</li><li>(g) -1 for "token rejected".
</li></ul>
<p class="inwebparagraph">Strictly speaking <code class="display"><span class="extract">ParseToken</span></code> is a shell routine which saves the current
state on the stack, and calling <code class="display"><span class="extract">ParseToken__</span></code> to do the actual work.
</p>
<p class="inwebparagraph">Once again the routine is traditionally divided into six letters, here named under
paragraphs "Parse Token Letter A", and so on.
</p>
<p class="inwebparagraph"></p>
<ul class="items"><li>(A) Analyse the token; handle all tokens not involving object lists and
break down others into elementary tokens
</li><li>(B) Begin parsing an object list
</li><li>(C) Parse descriptors (articles, pronouns, etc.) in the list
</li><li>(D) Parse an object name
</li><li>(E) Parse connectives (AND, BUT, etc.) and go back to (C)
</li><li>(F) Return the conclusion of parsing an object list
</li></ul>
<pre class="display">
<span class="plain">[ ParseTokenStopped x y;</span>
<span class="plain">if (wn&gt;WordCount()) return GPR_FAIL;</span>
<span class="plain">return ParseToken(x,y);</span>
<span class="plain">];</span>
<span class="plain">Global parsetoken_nesting = 0;</span>
<span class="plain">[ ParseToken given_ttype given_tdata token_n token i t rv;</span>
<span class="plain">if (parsetoken_nesting &gt; 0) {</span>
<span class="plain">! save match globals</span>
<span class="plain">@push match_from; @push token_filter; @push match_length;</span>
<span class="plain">@push number_of_classes; @push oops_from;</span>
<span class="plain">for (i=0: i&lt;number_matched: i++) {</span>
<span class="plain">t = match_list--&gt;i; @push t;</span>
<span class="plain">t = match_classes--&gt;i; @push t;</span>
<span class="plain">t = match_scores--&gt;i; @push t;</span>
<span class="plain">}</span>
<span class="plain">@push number_matched;</span>
<span class="plain">}</span>
<span class="plain">parsetoken_nesting++;</span>
<span class="plain">rv = ParseToken__(given_ttype, given_tdata, token_n, token);</span>
<span class="plain">parsetoken_nesting--;</span>
<span class="plain">if (parsetoken_nesting &gt; 0) {</span>
<span class="plain">! restore match globals</span>
<span class="plain">@pull number_matched;</span>
<span class="plain">for (i=number_matched-1: i&gt;=0: i--) {</span>
<span class="plain">@pull t; match_scores--&gt;i = t;</span>
<span class="plain">@pull t; match_classes--&gt;i = t;</span>
<span class="plain">@pull t; match_list--&gt;i = t;</span>
<span class="plain">}</span>
<span class="plain">@pull oops_from; @pull number_of_classes;</span>
<span class="plain">@pull match_length; @pull token_filter; @pull match_from;</span>
<span class="plain">}</span>
<span class="plain">return rv;</span>
<span class="plain">];</span>
<span class="plain">[ ParseToken__ given_ttype given_tdata token_n token</span>
<span class="plain">l o i j k and_parity single_object desc_wn many_flag</span>
<span class="plain">token_allows_multiple prev_indef_wanted;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP24"></a><b>&#167;24. Parse Token Letter A. </b>Analyse token; handle all not involving object lists, break down others.
</p>
<pre class="display">
<span class="plain">token_filter = 0;</span>
<span class="plain">parser_inflection = name;</span>
<span class="plain">switch (given_ttype) {</span>
<span class="plain">ELEMENTARY_TT:</span>
<span class="plain">switch (given_tdata) {</span>
<span class="plain">SPECIAL_TOKEN:</span>
<span class="plain">l = TryNumber(wn);</span>
<span class="plain">special_word = NextWord();</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (l ~= -1000)</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Read special as the number ", l, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (l == -1000) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Read special word at word number ", wn, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">l = special_word;</span>
<span class="plain">}</span>
<span class="plain">parsed_number = l;</span>
<span class="plain">return GPR_NUMBER;</span>
<span class="plain">NUMBER_TOKEN:</span>
<span class="plain">l=TryNumber(wn++);</span>
<span class="plain">if (l == -1000) {</span>
<span class="plain">etype = NUMBER_PE;</span>
<span class="plain">return GPR_FAIL;</span>
<span class="plain">}</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace&gt;=3) print " [Read number as ", l, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">parsed_number = l;</span>
<span class="plain">return GPR_NUMBER;</span>
<span class="plain">CREATURE_TOKEN:</span>
<span class="plain">if (action_to_be == ##Answer or ##Ask or ##AskFor or ##Tell)</span>
<span class="plain">scope_reason = TALKING_REASON;</span>
<span class="plain">TOPIC_TOKEN:</span>
<span class="plain">consult_from = wn;</span>
<span class="plain">if ((line_ttype--&gt;(token_n+1) ~= PREPOSITION_TT) &amp;&amp;</span>
<span class="plain">(line_token--&gt;(token_n+1) ~= ENDIT_TOKEN)) {</span>
<span class="plain">RunTimeProblem(RTP_TEXTTOKENTOOHARD);</span>
<span class="plain">return GPR_PREPOSITION;</span>
<span class="plain">}</span>
<span class="plain">do {</span>
<span class="plain">o = NextWordStopped();</span>
<span class="plain">} until (o == -1 || PrepositionChain(o, token_n+1) ~= -1);</span>
<span class="plain">wn--;</span>
<span class="plain">consult_words = wn-consult_from;</span>
<span class="plain">if (consult_words == 0) return GPR_FAIL;</span>
<span class="plain">if (action_to_be == ##Ask or ##Answer or ##Tell) {</span>
<span class="plain">o = wn; wn = consult_from; parsed_number = NextWord();</span>
<span class="plain">wn = o; return 1;</span>
<span class="plain">}</span>
<span class="plain">if (o==-1 &amp;&amp; (line_ttype--&gt;(token_n+1) == PREPOSITION_TT))</span>
<span class="plain">return GPR_FAIL; ! don't infer if required preposition is absent</span>
<span class="plain">return GPR_PREPOSITION;</span>
<span class="plain">}</span>
<span class="plain">PREPOSITION_TT:</span>
<span class="plain">! Is it an unnecessary alternative preposition, when a previous choice</span>
<span class="plain">! has already been matched?</span>
<span class="plain">if ((token-&gt;0) &amp; $10) return GPR_PREPOSITION;</span>
<span class="plain">! If we've run out of the player's input, but still have parameters to</span>
<span class="plain">! specify, we go into "infer" mode, remembering where we are and the</span>
<span class="plain">! preposition we are inferring...</span>
<span class="plain">if (wn &gt; num_words) {</span>
<span class="plain">if (inferfrom==0 &amp;&amp; parameters&lt;params_wanted) {</span>
<span class="plain">inferfrom = pcount; inferword = token;</span>
<span class="plain">pattern--&gt;pcount = REPARSE_CODE + VM_DictionaryAddressToNumber(given_tdata);</span>
<span class="plain">}</span>
<span class="plain">! If we are not inferring, then the line is wrong...</span>
<span class="plain">if (inferfrom == 0) return -1;</span>
<span class="plain">! If not, then the line is right but we mark in the preposition...</span>
<span class="plain">pattern--&gt;pcount = REPARSE_CODE + VM_DictionaryAddressToNumber(given_tdata);</span>
<span class="plain">return GPR_PREPOSITION;</span>
<span class="plain">}</span>
<span class="plain">o = NextWord();</span>
<span class="plain">pattern--&gt;pcount = REPARSE_CODE + VM_DictionaryAddressToNumber(o);</span>
<span class="plain">! Whereas, if the player has typed something here, see if it is the</span>
<span class="plain">! required preposition... if it's wrong, the line must be wrong,</span>
<span class="plain">! but if it's right, the token is passed (jump to finish this token).</span>
<span class="plain">if (o == given_tdata) return GPR_PREPOSITION;</span>
<span class="plain">if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION;</span>
<span class="plain">return -1;</span>
<span class="plain">GPR_TT:</span>
<span class="plain">l = indirect(given_tdata);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Outside parsing routine returned ", l, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">return l;</span>
<span class="plain">SCOPE_TT:</span>
<span class="plain">scope_token = given_tdata;</span>
<span class="plain">scope_stage = 1;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Scope routine called at stage 1]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">l = indirect(scope_token);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Scope routine returned multiple-flag of ", l, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;</span>
<span class="plain">ATTR_FILTER_TT:</span>
<span class="plain">token_filter = 1 + given_tdata;</span>
<span class="plain">given_tdata = NOUN_TOKEN;</span>
<span class="plain">ROUTINE_FILTER_TT:</span>
<span class="plain">token_filter = given_tdata;</span>
<span class="plain">given_tdata = NOUN_TOKEN;</span>
<span class="plain">} ! end of switch(given_ttype)</span>
<span class="plain">token = given_tdata;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP25"></a><b>&#167;25. Parse Token Letter B. </b>Begin parsing an object list.
</p>
<pre class="display">
<span class="plain">! There are now three possible ways we can be here:</span>
<span class="plain">! parsing an elementary token other than "special" or "number";</span>
<span class="plain">! parsing a scope token;</span>
<span class="plain">! parsing a noun-filter token (either by routine or attribute).</span>
<span class="plain">!</span>
<span class="plain">! In each case, token holds the type of elementary parse to</span>
<span class="plain">! perform in matching one or more objects, and</span>
<span class="plain">! token_filter is 0 (default), an attribute + 1 for an attribute filter</span>
<span class="plain">! or a routine address for a routine filter.</span>
<span class="plain">token_allows_multiple = false;</span>
<span class="plain">if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)</span>
<span class="plain">token_allows_multiple = true;</span>
<span class="plain">many_flag = false; and_parity = true; dont_infer = false;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP26"></a><b>&#167;26. Parse Token Letter C. </b>Parse descriptors (articles, pronouns, etc.) in the list.
</p>
<pre class="display">
<span class="plain">! We expect to find a list of objects next in what the player's typed.</span>
<span class="plain">.ObjectList;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Object list from word ", wn, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">! Take an advance look at the next word: if it's "it" or "them", and these</span>
<span class="plain">! are unset, set the appropriate error number and give up on the line</span>
<span class="plain">! (if not, these are still parsed in the usual way - it is not assumed</span>
<span class="plain">! that they still refer to something in scope)</span>
<span class="plain">o = NextWord(); wn--;</span>
<span class="plain">pronoun_word = NULL; pronoun_obj = NULL;</span>
<span class="plain">l = PronounValue(o);</span>
<span class="plain">if (l ~= 0) {</span>
<span class="plain">pronoun_word = o; pronoun_obj = l;</span>
<span class="plain">if (l == NULL) {</span>
<span class="plain">! Don't assume this is a use of an unset pronoun until the</span>
<span class="plain">! descriptors have been checked, because it might be an</span>
<span class="plain">! article (or some such) instead</span>
<span class="plain">for (l=1 : l&lt;=LanguageDescriptors--&gt;0 : l=l+4)</span>
<span class="plain">if (o == LanguageDescriptors--&gt;l) jump AssumeDescriptor;</span>
<span class="plain">pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;</span>
<span class="plain">etype = VAGUE_PE;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Stop: unset pronoun]^";</span>
<span class="plain">return GPR_FAIL;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">.AssumeDescriptor;</span>
<span class="plain">if (o == ME1__WD or ME2__WD or ME3__WD) { pronoun_word = o; pronoun_obj = player; }</span>
<span class="plain">allow_plurals = true; desc_wn = wn;</span>
<span class="plain">.TryAgain;</span>
<span class="plain">! First, we parse any descriptive words (like "the", "five" or "every"):</span>
<span class="plain">l = Descriptors(token_allows_multiple);</span>
<span class="plain">if (l ~= 0) { etype = l; return 0; }</span>
<span class="plain">.TryAgain2;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP27"></a><b>&#167;27. Parse Token Letter D. </b>Parse an object name.
</p>
<pre class="display">
<span class="plain">! This is an actual specified object, and is therefore where a typing error</span>
<span class="plain">! is most likely to occur, so we set:</span>
<span class="plain">oops_from = wn;</span>
<span class="plain">! So, two cases. Case 1: token not equal to "held" (so, no implicit takes)</span>
<span class="plain">! but we may well be dealing with multiple objects</span>
<span class="plain">! In either case below we use NounDomain, giving it the token number as</span>
<span class="plain">! context, and two places to look: among the actor's possessions, and in the</span>
<span class="plain">! present location. (Note that the order depends on which is likeliest.)</span>
<span class="plain">if (token ~= HELD_TOKEN) {</span>
<span class="plain">i = multiple_object--&gt;0;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Calling NounDomain on location and actor]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">l = NounDomain(actors_location, actor, token);</span>
<span class="plain">if (l == REPARSE_CODE) return l; ! Reparse after Q&amp;A</span>
<span class="plain">if (indef_wanted == INDEF_ALL_WANTED &amp;&amp; l == 0 &amp;&amp; number_matched == 0)</span>
<span class="plain">l = 1; ! ReviseMulti if TAKE ALL FROM empty container</span>
<span class="plain">if (token_allows_multiple &amp;&amp; ~~multiflag) {</span>
<span class="plain">if (best_etype==MULTI_PE) best_etype=STUCK_PE;</span>
<span class="plain">multiflag = true;</span>
<span class="plain">}</span>
<span class="plain">if (l == 0) {</span>
<span class="plain">if (indef_possambig) {</span>
<span class="plain">ResetDescriptors();</span>
<span class="plain">wn = desc_wn;</span>
<span class="plain">jump TryAgain2;</span>
<span class="plain">}</span>
<span class="plain">if (etype == MULTI_PE &amp;&amp; multiflag) etype = STUCK_PE;</span>
<span class="plain">etype=CantSee();</span>
<span class="plain">jump FailToken;</span>
<span class="plain">} ! Choose best error</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) {</span>
<span class="plain">if (l &gt; 1) print " [ND returned ", (the) l, "]^";</span>
<span class="plain">else {</span>
<span class="plain">print " [ND appended to the multiple object list:^";</span>
<span class="plain">k = multiple_object--&gt;0;</span>
<span class="plain">for (j=i+1 : j&lt;=k : j++)</span>
<span class="plain">print " Entry ", j, ": ", (The) multiple_object--&gt;j,</span>
<span class="plain">" (", multiple_object--&gt;j, ")^";</span>
<span class="plain">print " List now has size ", k, "]^";</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (l == 1) {</span>
<span class="plain">if (~~many_flag) many_flag = true;</span>
<span class="plain">else { ! Merge with earlier ones</span>
<span class="plain">k = multiple_object--&gt;0; ! (with either parity)</span>
<span class="plain">multiple_object--&gt;0 = i;</span>
<span class="plain">for (j=i+1 : j&lt;=k : j++) {</span>
<span class="plain">if (and_parity) MultiAdd(multiple_object--&gt;j);</span>
<span class="plain">else MultiSub(multiple_object--&gt;j);</span>
<span class="plain">}</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3)</span>
<span class="plain">print " [Merging ", k-i, " new objects to the ", i, " old ones]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">else {</span>
<span class="plain">! A single object was indeed found</span>
<span class="plain">if (match_length == 0 &amp;&amp; indef_possambig) {</span>
<span class="plain">! So the answer had to be inferred from no textual data,</span>
<span class="plain">! and we know that there was an ambiguity in the descriptor</span>
<span class="plain">! stage (such as a word which could be a pronoun being</span>
<span class="plain">! parsed as an article or possessive). It's worth having</span>
<span class="plain">! another go.</span>
<span class="plain">ResetDescriptors();</span>
<span class="plain">wn = desc_wn;</span>
<span class="plain">jump TryAgain2;</span>
<span class="plain">}</span>
<span class="plain">if ((token == CREATURE_TOKEN) &amp;&amp; (CreatureTest(l) == 0)) {</span>
<span class="plain">etype = ANIMA_PE;</span>
<span class="plain">jump FailToken;</span>
<span class="plain">} ! Animation is required</span>
<span class="plain">if (~~many_flag) single_object = l;</span>
<span class="plain">else {</span>
<span class="plain">if (and_parity) MultiAdd(l); else MultiSub(l);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Combining ", (the) l, " with list]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">else {</span>
<span class="plain">! Case 2: token is "held" (which fortunately can't take multiple objects)</span>
<span class="plain">! and may generate an implicit take</span>
<span class="plain">l = NounDomain(actor,actors_location,token); ! Same as above...</span>
<span class="plain">if (l == REPARSE_CODE) return l;</span>
<span class="plain">if (l == 0) {</span>
<span class="plain">if (indef_possambig) {</span>
<span class="plain">ResetDescriptors();</span>
<span class="plain">wn = desc_wn;</span>
<span class="plain">jump TryAgain2;</span>
<span class="plain">}</span>
<span class="plain">etype = CantSee(); jump FailToken; ! Choose best error</span>
<span class="plain">}</span>
<span class="plain">! ...until it produces something not held by the actor. Then an implicit</span>
<span class="plain">! take must be tried. If this is already happening anyway, things are too</span>
<span class="plain">! confused and we have to give up (but saving the oops marker so as to get</span>
<span class="plain">! it on the right word afterwards).</span>
<span class="plain">! The point of this last rule is that a sequence like</span>
<span class="plain">!</span>
<span class="plain">! &gt; read newspaper</span>
<span class="plain">! (taking the newspaper first)</span>
<span class="plain">! The dwarf unexpectedly prevents you from taking the newspaper!</span>
<span class="plain">!</span>
<span class="plain">! should not be allowed to go into an infinite repeat - read becomes</span>
<span class="plain">! take then read, but take has no effect, so read becomes take then read...</span>
<span class="plain">! Anyway for now all we do is record the number of the object to take.</span>
<span class="plain">o = parent(l);</span>
<span class="plain">if (o ~= actor) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Allowing object ", (the) l, " for now]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">single_object = l;</span>
<span class="plain">} ! end of if (token ~= HELD_TOKEN) else</span>
<span class="plain">! The following moves the word marker to just past the named object...</span>
<span class="plain">! if (match_from ~= oops_from) print match_from, " vs ", oops_from, "^";</span>
<span class="plain">! wn = oops_from + match_length;</span>
<span class="plain">wn = match_from + match_length;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP28"></a><b>&#167;28. Parse Token Letter E. </b>Parse connectives (AND, BUT, etc.) and go back to (C).
</p>
<pre class="display">
<span class="plain">! Object(s) specified now: is that the end of the list, or have we reached</span>
<span class="plain">! "and", "but" and so on? If so, create a multiple-object list if we</span>
<span class="plain">! haven't already (and are allowed to).</span>
<span class="plain">.NextInList;</span>
<span class="plain">o = NextWord();</span>
<span class="plain">if (o == AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD or comma_word) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Read connective '", (address) o, "']^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (~~token_allows_multiple) {</span>
<span class="plain">if (multiflag) jump PassToken; ! give UPTO_PE error</span>
<span class="plain">etype=MULTI_PE;</span>
<span class="plain">jump FailToken;</span>
<span class="plain">}</span>
<span class="plain">if (o == BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;</span>
<span class="plain">if (~~many_flag) {</span>
<span class="plain">multiple_object--&gt;0 = 1;</span>
<span class="plain">multiple_object--&gt;1 = single_object;</span>
<span class="plain">many_flag = true;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Making new list from ", (the) single_object, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">dont_infer = true; inferfrom=0; ! Don't print (inferences)</span>
<span class="plain">jump ObjectList; ! And back around</span>
<span class="plain">}</span>
<span class="plain">wn--; ! Word marker back to first not-understood word</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP29"></a><b>&#167;29. Parse Token Letter F. </b>Return the conclusion of parsing an object list.
</p>
<pre class="display">
<span class="plain">! Happy or unhappy endings:</span>
<span class="plain">.PassToken;</span>
<span class="plain">if (many_flag) {</span>
<span class="plain">single_object = GPR_MULTIPLE;</span>
<span class="plain">multi_context = token;</span>
<span class="plain">}</span>
<span class="plain">else {</span>
<span class="plain">if (indef_mode == 1 &amp;&amp; indef_type &amp; PLURAL_BIT ~= 0) {</span>
<span class="plain">if (token == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) multi_context = token;</span>
<span class="plain">if (indef_wanted &lt; INDEF_ALL_WANTED &amp;&amp; indef_wanted &gt; 1) {</span>
<span class="plain">multi_had = 1; multi_wanted = indef_wanted;</span>
<span class="plain">etype = TOOFEW_PE;</span>
<span class="plain">jump FailToken;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">return single_object;</span>
<span class="plain">.FailToken;</span>
<span class="plain">! If we were only guessing about it being a plural, try again but only</span>
<span class="plain">! allowing singulars (so that words like "six" are not swallowed up as</span>
<span class="plain">! Descriptors)</span>
<span class="plain">if (allow_plurals &amp;&amp; indef_guess_p == 1) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " [Retrying singulars after failure ", etype, "]^";</span>
<span class="plain">#Endif;</span>
<span class="plain">prev_indef_wanted = indef_wanted;</span>
<span class="plain">allow_plurals = false;</span>
<span class="plain">wn = desc_wn;</span>
<span class="plain">jump TryAgain;</span>
<span class="plain">}</span>
<span class="plain">if ((indef_wanted &gt; 0 || prev_indef_wanted &gt; 0) &amp;&amp; (~~multiflag)) etype = MULTI_PE;</span>
<span class="plain">return GPR_FAIL;</span>
<span class="plain">]; ! end of ParseToken__</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP30"></a><b>&#167;30. Descriptors. </b>In grammatical terms, a descriptor appears at the front of an English noun
phrase and clarifies the quantity or specific identity of what is referred
to: for instance, my mirror, the dwarf, that woman.
(Numbers, as in four duets, are also descriptors in linguistics:
but the I6 parser doesn't handle them that way.)
</p>
<p class="inwebparagraph">Slightly unfortunately, the bitmap constants used for descriptors in the
I6 parser have names in the form <code class="display"><span class="extract">*_BIT</span></code>, coinciding with the names of
style bits in the list-writer: but they never occur in the same context.
</p>
<p class="inwebparagraph">The actual words used as descriptors are read from tables in the language
definition. <code class="display"><span class="extract">ArticleDescriptors</span></code> uses this table to move current word
marker past a run of one or more descriptors which refer to the definite
or indefinite article.
</p>
<pre class="display">
<span class="plain">Constant OTHER_BIT = 1; ! These will be used in Adjudicate()</span>
<span class="plain">Constant MY_BIT = 2; ! to disambiguate choices</span>
<span class="plain">Constant THAT_BIT = 4;</span>
<span class="plain">Constant PLURAL_BIT = 8;</span>
<span class="plain">Constant LIT_BIT = 16;</span>
<span class="plain">Constant UNLIT_BIT = 32;</span>
<span class="plain">[ ResetDescriptors;</span>
<span class="plain">indef_mode = 0; indef_type = 0; indef_wanted = 0; indef_guess_p = 0;</span>
<span class="plain">indef_possambig = false;</span>
<span class="plain">indef_owner = nothing;</span>
<span class="plain">indef_cases = $$111111111111;</span>
<span class="plain">indef_nspec_at = 0;</span>
<span class="plain">];</span>
<span class="plain">[ ArticleDescriptors o x flag cto type n;</span>
<span class="plain">if (wn &gt; num_words) return 0;</span>
<span class="plain">for (flag=true : flag :) {</span>
<span class="plain">o = NextWordStopped(); flag = false;</span>
<span class="plain">for (x=1 : x&lt;=LanguageDescriptors--&gt;0 : x=x+4)</span>
<span class="plain">if (o == LanguageDescriptors--&gt;x) {</span>
<span class="plain">type = LanguageDescriptors--&gt;(x+2);</span>
<span class="plain">if (type == DEFART_PK or INDEFART_PK) flag = true;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">wn--;</span>
<span class="plain">return 0;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP31"></a><b>&#167;31. Parsing Descriptors. </b>The <code class="display"><span class="extract">Descriptors()</span></code> routine parses the descriptors at the head of a noun
phrase, leaving the current word marker <code class="display"><span class="extract">wn</span></code> at the first word of the
noun phrase's body. It is allowed to set up for a plural only if <code class="display"><span class="extract">allow_p</span></code>
is set; it returns a parser error number, or 0 if no error occurred.
</p>
<pre class="display">
<span class="plain">[ Descriptors o x flag cto type n;</span>
<span class="plain">ResetDescriptors();</span>
<span class="plain">if (wn &gt; num_words) return 0;</span>
<span class="plain">for (flag=true : flag :) {</span>
<span class="plain">o = NextWordStopped(); flag = false;</span>
<span class="plain">for (x=1 : x&lt;=LanguageDescriptors--&gt;0 : x=x+4)</span>
<span class="plain">if (o == LanguageDescriptors--&gt;x) {</span>
<span class="plain">flag = true;</span>
<span class="plain">type = LanguageDescriptors--&gt;(x+2);</span>
<span class="plain">if (type ~= DEFART_PK) indef_mode = true;</span>
<span class="plain">indef_possambig = true;</span>
<span class="plain">indef_cases = indef_cases &amp; (LanguageDescriptors--&gt;(x+1));</span>
<span class="plain">if (type == POSSESS_PK) {</span>
<span class="plain">cto = LanguageDescriptors--&gt;(x+3);</span>
<span class="plain">switch (cto) {</span>
<span class="plain">0: indef_type = indef_type | MY_BIT;</span>
<span class="plain">1: indef_type = indef_type | THAT_BIT;</span>
<span class="plain">default:</span>
<span class="plain">indef_owner = PronounValue(cto);</span>
<span class="plain">if (indef_owner == NULL) indef_owner = InformParser;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (type == light) indef_type = indef_type | LIT_BIT;</span>
<span class="plain">if (type == -light) indef_type = indef_type | UNLIT_BIT;</span>
<span class="plain">}</span>
<span class="plain">if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {</span>
<span class="plain">indef_mode = 1; flag = 1;</span>
<span class="plain">indef_type = indef_type | OTHER_BIT;</span>
<span class="plain">}</span>
<span class="plain">if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {</span>
<span class="plain">indef_mode = 1; flag = 1; indef_wanted = INDEF_ALL_WANTED;</span>
<span class="plain">if (take_all_rule == 1) take_all_rule = 2;</span>
<span class="plain">indef_type = indef_type | PLURAL_BIT;</span>
<span class="plain">}</span>
<span class="plain">if (allow_plurals) {</span>
<span class="plain">if (NextWordStopped() ~= -1 or THEN1__WD) { wn--; n = TryNumber(wn-1); } else { n=0; wn--; }</span>
<span class="plain">if (n == 1) { indef_mode = 1; flag = 1; }</span>
<span class="plain">if (n &gt; 1) {</span>
<span class="plain">indef_guess_p = 1;</span>
<span class="plain">indef_mode = 1; flag = 1; indef_wanted = n;</span>
<span class="plain">indef_nspec_at = wn-1;</span>
<span class="plain">indef_type = indef_type | PLURAL_BIT;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (flag == 1 &amp;&amp; NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)</span>
<span class="plain">wn--; ! Skip 'of' after these</span>
<span class="plain">}</span>
<span class="plain">wn--;</span>
<span class="plain">return 0;</span>
<span class="plain">];</span>
<span class="plain">[ SafeSkipDescriptors;</span>
<span class="plain">@push indef_mode; @push indef_type; @push indef_wanted;</span>
<span class="plain">@push indef_guess_p; @push indef_possambig; @push indef_owner;</span>
<span class="plain">@push indef_cases; @push indef_nspec_at;</span>
<span class="plain">Descriptors();</span>
<span class="plain">@pull indef_nspec_at; @pull indef_cases;</span>
<span class="plain">@pull indef_owner; @pull indef_possambig; @pull indef_guess_p;</span>
<span class="plain">@pull indef_wanted; @pull indef_type; @pull indef_mode;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP32"></a><b>&#167;32. Preposition Chain. </b>A small utility for runs of prepositions.
</p>
<pre class="display">
<span class="plain">[ PrepositionChain wd index;</span>
<span class="plain">if (line_tdata--&gt;index == wd) return wd;</span>
<span class="plain">if ((line_token--&gt;index)-&gt;0 &amp; $20 == 0) return -1;</span>
<span class="plain">do {</span>
<span class="plain">if (line_tdata--&gt;index == wd) return wd;</span>
<span class="plain">index++;</span>
<span class="plain">} until ((line_token--&gt;index == ENDIT_TOKEN) || (((line_token--&gt;index)-&gt;0 &amp; $10) == 0));</span>
<span class="plain">return -1;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP33"></a><b>&#167;33. Creature. </b>Will this object do for an I6 <code class="display"><span class="extract">creature</span></code> token? (In I7 terms, this affects
the tokens "[someone]", "[somebody]", "[anyone]" and "[anybody]".)
</p>
<pre class="display">
<span class="plain">[ CreatureTest obj;</span>
<span class="plain">if (obj has animate) rtrue;</span>
<span class="plain">if (obj hasnt talkable) rfalse;</span>
<span class="plain">if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue;</span>
<span class="plain">rfalse;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP34"></a><b>&#167;34. Noun Domain. </b><code class="display"><span class="extract">NounDomain</span></code> does the most substantial part of parsing an object name.
It is given two "domains" &mdash; usually a location and then the actor who is
looking &mdash; and a context (i.e. token type), and returns:
</p>
<p class="inwebparagraph"></p>
<ul class="items"><li>(a) 0 if no match at all could be made,
</li><li>(b) 1 if a multiple object was made,
</li><li>(c) k if object k was the one decided upon,
</li><li>(d) <code class="display"><span class="extract">REPARSE_CODE</span></code> if it asked a question of the player and consequently
rewrote the player's input, so that the whole parser should start again
on the rewritten input.
</li></ul>
<p class="inwebparagraph">In case (c), <code class="display"><span class="extract">NounDomain</span></code> also sets the variable <code class="display"><span class="extract">length_of_noun</span></code> to the
number of words in the input text matched to the noun. In case (b),
the multiple objects are added to <code class="display"><span class="extract">multiple_object</span></code> by hand (not by <code class="display"><span class="extract">MultiAdd</span></code>,
because we want to allow duplicates).
</p>
<pre class="display">
<span class="plain">[ NounDomain domain1 domain2 context dont_ask</span>
<span class="plain">first_word i j k l answer_words marker;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) {</span>
<span class="plain">print " [NounDomain called at word ", wn, "^";</span>
<span class="plain">print " ";</span>
<span class="plain">if (indef_mode) {</span>
<span class="plain">print "seeking indefinite object: ";</span>
<span class="plain">if (indef_type &amp; OTHER_BIT) print "other ";</span>
<span class="plain">if (indef_type &amp; MY_BIT) print "my ";</span>
<span class="plain">if (indef_type &amp; THAT_BIT) print "that ";</span>
<span class="plain">if (indef_type &amp; PLURAL_BIT) print "plural ";</span>
<span class="plain">if (indef_type &amp; LIT_BIT) print "lit ";</span>
<span class="plain">if (indef_type &amp; UNLIT_BIT) print "unlit ";</span>
<span class="plain">if (indef_owner ~= 0) print "owner:", (name) indef_owner;</span>
<span class="plain">new_line;</span>
<span class="plain">print " number wanted: ";</span>
<span class="plain">if (indef_wanted == INDEF_ALL_WANTED) print "all"; else print indef_wanted;</span>
<span class="plain">new_line;</span>
<span class="plain">print " most likely GNAs of names: ", indef_cases, "^";</span>
<span class="plain">}</span>
<span class="plain">else print "seeking definite object^";</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">match_length = 0; number_matched = 0; match_from = wn;</span>
<span class="plain">SearchScope(domain1, domain2, context);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " [ND made ", number_matched, " matches]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">wn = match_from+match_length;</span>
<span class="plain">! If nothing worked at all, leave with the word marker skipped past the</span>
<span class="plain">! first unmatched word...</span>
<span class="plain">if (number_matched == 0) { wn++; rfalse; }</span>
<span class="plain">! Suppose that there really were some words being parsed (i.e., we did</span>
<span class="plain">! not just infer). If so, and if there was only one match, it must be</span>
<span class="plain">! right and we return it...</span>
<span class="plain">if (match_from &lt;= num_words) {</span>
<span class="plain">if (number_matched == 1) {</span>
<span class="plain">i=match_list--&gt;0;</span>
<span class="plain">return i;</span>
<span class="plain">}</span>
<span class="plain">! ...now suppose that there was more typing to come, i.e. suppose that</span>
<span class="plain">! the user entered something beyond this noun. If nothing ought to follow,</span>
<span class="plain">! then there must be a mistake, (unless what does follow is just a full</span>
<span class="plain">! stop, and or comma)</span>
<span class="plain">if (wn &lt;= num_words) {</span>
<span class="plain">i = NextWord(); wn--;</span>
<span class="plain">if (i ~= AND1__WD or AND2__WD or AND3__WD or comma_word</span>
<span class="plain">or THEN1__WD or THEN2__WD or THEN3__WD</span>
<span class="plain">or BUT1__WD or BUT2__WD or BUT3__WD) {</span>
<span class="plain">if (lookahead == ENDIT_TOKEN) rfalse;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! Now look for a good choice, if there's more than one choice...</span>
<span class="plain">number_of_classes = 0;</span>
<span class="plain">if (number_matched == 1) {</span>
<span class="plain">i = match_list--&gt;0;</span>
<span class="plain">if (indef_mode == 1 &amp;&amp; indef_type &amp; PLURAL_BIT ~= 0) {</span>
<span class="plain">if (context == MULTI_TOKEN or MULTIHELD_TOKEN or</span>
<span class="plain">MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN or</span>
<span class="plain">NOUN_TOKEN or HELD_TOKEN or CREATURE_TOKEN) {</span>
<span class="plain">BeginActivity(DECIDING_WHETHER_ALL_INC_ACT, i);</span>
<span class="plain">if ((ForActivity(DECIDING_WHETHER_ALL_INC_ACT, i)) &amp;&amp;</span>
<span class="plain">(RulebookFailed())) rfalse;</span>
<span class="plain">EndActivity(DECIDING_WHETHER_ALL_INC_ACT, i);</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (number_matched &gt; 1) {</span>
<span class="plain">i = true;</span>
<span class="plain">if (number_matched &gt; 1)</span>
<span class="plain">for (j=0 : j&lt;number_matched-1 : j++)</span>
<span class="plain">if (Identical(match_list--&gt;j, match_list--&gt;(j+1)) == false)</span>
<span class="plain">i = false;</span>
<span class="plain">if (i) dont_infer = true;</span>
<span class="plain">i = Adjudicate(context);</span>
<span class="plain">if (i == -1) rfalse;</span>
<span class="plain">if (i == 1) rtrue; ! Adjudicate has made a multiple</span>
<span class="plain">! object, and we pass it on</span>
<span class="plain">}</span>
<span class="plain">! If i is non-zero here, one of two things is happening: either</span>
<span class="plain">! (a) an inference has been successfully made that object i is</span>
<span class="plain">! the intended one from the user's specification, or</span>
<span class="plain">! (b) the user finished typing some time ago, but we've decided</span>
<span class="plain">! on i because it's the only possible choice.</span>
<span class="plain">! In either case we have to keep the pattern up to date,</span>
<span class="plain">! note that an inference has been made and return.</span>
<span class="plain">! (Except, we don't note which of a pile of identical objects.)</span>
<span class="plain">if (i ~= 0) {</span>
<span class="plain">if (dont_infer) return i;</span>
<span class="plain">if (inferfrom == 0) inferfrom=pcount;</span>
<span class="plain">pattern--&gt;pcount = i;</span>
<span class="plain">return i;</span>
<span class="plain">}</span>
<span class="plain">if (dont_ask) return match_list--&gt;0;</span>
<span class="plain">! If we get here, there was no obvious choice of object to make. If in</span>
<span class="plain">! fact we've already gone past the end of the player's typing (which</span>
<span class="plain">! means the match list must contain every object in scope, regardless</span>
<span class="plain">! of its name), then it's foolish to give an enormous list to choose</span>
<span class="plain">! from - instead we go and ask a more suitable question...</span>
<span class="plain">if (match_from &gt; num_words) jump Incomplete;</span>
<span class="plain">! Now we print up the question, using the equivalence classes as worked</span>
<span class="plain">! out by Adjudicate() so as not to repeat ourselves on plural objects...</span>
<span class="plain">BeginActivity(ASKING_WHICH_DO_YOU_MEAN_ACT);</span>
<span class="plain">if (ForActivity(ASKING_WHICH_DO_YOU_MEAN_ACT)) jump SkipWhichQuestion;</span>
<span class="plain">j = 1; marker = 0;</span>
<span class="plain">for (i=1 : i&lt;=number_of_classes : i++) {</span>
<span class="plain">while (((match_classes--&gt;marker) ~= i) &amp;&amp; ((match_classes--&gt;marker) ~= -i))</span>
<span class="plain">marker++;</span>
<span class="plain">if (match_list--&gt;marker hasnt animate) j = 0;</span>
<span class="plain">}</span>
<span class="plain">if (j) PARSER_CLARIF_INTERNAL_RM('A');</span>
<span class="plain">else PARSER_CLARIF_INTERNAL_RM('B');</span>
<span class="plain">j = number_of_classes; marker = 0;</span>
<span class="plain">for (i=1 : i&lt;=number_of_classes : i++) {</span>
<span class="plain">while (((match_classes--&gt;marker) ~= i) &amp;&amp; ((match_classes--&gt;marker) ~= -i)) marker++;</span>
<span class="plain">k = match_list--&gt;marker;</span>
<span class="plain">if (match_classes--&gt;marker &gt; 0) print (the) k; else print (a) k;</span>
<span class="plain">if (i &lt; j-1) print ", ";</span>
<span class="plain">if (i == j-1) {</span>
<span class="plain">if (TEMPLATE_CONFIGURATION_BITMAP &amp; SERIAL_COMMA_TCBIT) {</span>
<span class="plain">if (j ~= 2) print ",";</span>
<span class="plain">}</span>
<span class="plain">PARSER_CLARIF_INTERNAL_RM('H');</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">print "?^";</span>
<span class="plain">.SkipWhichQuestion; EndActivity(ASKING_WHICH_DO_YOU_MEAN_ACT);</span>
<span class="plain">! ...and get an answer:</span>
<span class="plain">.WhichOne;</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">for (i=2 : i&lt;INPUT_BUFFER_LEN : i++) buffer2-&gt;i = ' ';</span>
<span class="plain">#Endif; ! TARGET_ZCODE</span>
<span class="plain">answer_words=Keyboard(buffer2, parse2);</span>
<span class="plain">! Conveniently, parse2--&gt;1 is the first word in both ZCODE and GLULX.</span>
<span class="plain">first_word = (parse2--&gt;1);</span>
<span class="plain">! Take care of "all", because that does something too clever here to do</span>
<span class="plain">! later on:</span>
<span class="plain">if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {</span>
<span class="plain">if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {</span>
<span class="plain">l = multiple_object--&gt;0;</span>
<span class="plain">for (i=0 : i&lt;number_matched &amp;&amp; l+i&lt;MATCH_LIST_WORDS : i++) {</span>
<span class="plain">k = match_list--&gt;i;</span>
<span class="plain">multiple_object--&gt;(i+1+l) = k;</span>
<span class="plain">}</span>
<span class="plain">multiple_object--&gt;0 = i+l;</span>
<span class="plain">rtrue;</span>
<span class="plain">}</span>
<span class="plain">PARSER_CLARIF_INTERNAL_RM('C');</span>
<span class="plain">jump WhichOne;</span>
<span class="plain">}</span>
<span class="plain">! Look for a comma, and interpret this as a fresh conversation command</span>
<span class="plain">! if so:</span>
<span class="plain">for (i=1 : i&lt;=answer_words : i++)</span>
<span class="plain">if (WordFrom(i, parse2) == comma_word) {</span>
<span class="plain">VM_CopyBuffer(buffer, buffer2);</span>
<span class="plain">jump RECONSTRUCT_INPUT;</span>
<span class="plain">}</span>
<span class="plain">! If the first word of the reply can be interpreted as a verb, then</span>
<span class="plain">! assume that the player has ignored the question and given a new</span>
<span class="plain">! command altogether.</span>
<span class="plain">! (This is one time when it's convenient that the directions are</span>
<span class="plain">! not themselves verbs - thus, "north" as a reply to "Which, the north</span>
<span class="plain">! or south door" is not treated as a fresh command but as an answer.)</span>
<span class="plain">if (first_word == 0) {</span>
<span class="plain">j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;</span>
<span class="plain">}</span>
<span class="plain">if (first_word ~= 0) {</span>
<span class="plain">j = first_word-&gt;#dict_par1;</span>
<span class="plain">if ((0 ~= j&amp;1) &amp;&amp; ~~LanguageVerbMayBeName(first_word)) {</span>
<span class="plain">VM_CopyBuffer(buffer, buffer2);</span>
<span class="plain">jump RECONSTRUCT_INPUT;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! Now we insert the answer into the original typed command, as</span>
<span class="plain">! words additionally describing the same object</span>
<span class="plain">! (eg, &gt; take red button</span>
<span class="plain">! Which one, ...</span>
<span class="plain">! &gt; music</span>
<span class="plain">! becomes "take music red button". The parser will thus have three</span>
<span class="plain">! words to work from next time, not two.)</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">k = WordAddress(match_from) - buffer; l=buffer2-&gt;1+1;</span>
<span class="plain">for (j=buffer + buffer-&gt;0 - 1 : j&gt;=buffer+k+l : j--) j-&gt;0 = 0-&gt;(j-l);</span>
<span class="plain">for (i=0 : i&lt;l : i++) buffer-&gt;(k+i) = buffer2-&gt;(2+i);</span>
<span class="plain">buffer-&gt;(k+l-1) = ' ';</span>
<span class="plain">buffer-&gt;1 = buffer-&gt;1 + l;</span>
<span class="plain">if (buffer-&gt;1 &gt;= (buffer-&gt;0 - 1)) buffer-&gt;1 = buffer-&gt;0;</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">k = WordAddress(match_from) - buffer;</span>
<span class="plain">l = (buffer2--&gt;0) + 1;</span>
<span class="plain">for (j=buffer+INPUT_BUFFER_LEN-1 : j&gt;=buffer+k+l : j--) j-&gt;0 = j-&gt;(-l);</span>
<span class="plain">for (i=0 : i&lt;l : i++) buffer-&gt;(k+i) = buffer2-&gt;(WORDSIZE+i);</span>
<span class="plain">buffer-&gt;(k+l-1) = ' ';</span>
<span class="plain">buffer--&gt;0 = buffer--&gt;0 + l;</span>
<span class="plain">if (buffer--&gt;0 &gt; (INPUT_BUFFER_LEN-WORDSIZE)) buffer--&gt;0 = (INPUT_BUFFER_LEN-WORDSIZE);</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">! Having reconstructed the input, we warn the parser accordingly</span>
<span class="plain">! and get out.</span>
<span class="plain">.RECONSTRUCT_INPUT;</span>
<span class="plain">num_words = WordCount(); players_command = 100 + num_words;</span>
<span class="plain">wn = 1;</span>
<span class="plain">#Ifdef LanguageToInformese;</span>
<span class="plain">LanguageToInformese();</span>
<span class="plain">! Re-tokenise:</span>
<span class="plain">VM_Tokenise(buffer,parse);</span>
<span class="plain">#Endif; ! LanguageToInformese</span>
<span class="plain">num_words = WordCount(); players_command = 100 + num_words;</span>
<span class="plain">actors_location = ScopeCeiling(player);</span>
<span class="plain">FollowRulebook(Activity_after_rulebooks--&gt;READING_A_COMMAND_ACT);</span>
<span class="plain">return REPARSE_CODE;</span>
<span class="plain">! Now we come to the question asked when the input has run out</span>
<span class="plain">! and can't easily be guessed (eg, the player typed "take" and there</span>
<span class="plain">! were plenty of things which might have been meant).</span>
<span class="plain">.Incomplete;</span>
<span class="plain">if (context == CREATURE_TOKEN) PARSER_CLARIF_INTERNAL_RM('D', actor);</span>
<span class="plain">else PARSER_CLARIF_INTERNAL_RM('E', actor);</span>
<span class="plain">new_line;</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">for (i=2 : i&lt;INPUT_BUFFER_LEN : i++) buffer2-&gt;i=' ';</span>
<span class="plain">#Endif; ! TARGET_ZCODE</span>
<span class="plain">answer_words = Keyboard(buffer2, parse2);</span>
<span class="plain">! Look for a comma, and interpret this as a fresh conversation command</span>
<span class="plain">! if so:</span>
<span class="plain">for (i=1 : i&lt;=answer_words : i++)</span>
<span class="plain">if (WordFrom(i, parse2) == comma_word) {</span>
<span class="plain">VM_CopyBuffer(buffer, buffer2);</span>
<span class="plain">jump RECONSTRUCT_INPUT;</span>
<span class="plain">}</span>
<span class="plain">first_word=(parse2--&gt;1);</span>
<span class="plain">if (first_word==0) {</span>
<span class="plain">j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;</span>
<span class="plain">}</span>
<span class="plain">! Once again, if the reply looks like a command, give it to the</span>
<span class="plain">! parser to get on with and forget about the question...</span>
<span class="plain">if (first_word ~= 0) {</span>
<span class="plain">j = first_word-&gt;#dict_par1;</span>
<span class="plain">if ((0 ~= j&amp;1) &amp;&amp; ~~LanguageVerbMayBeName(first_word)) {</span>
<span class="plain">VM_CopyBuffer(buffer, buffer2);</span>
<span class="plain">jump RECONSTRUCT_INPUT;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! ...but if we have a genuine answer, then:</span>
<span class="plain">!</span>
<span class="plain">! (1) we must glue in text suitable for anything that's been inferred.</span>
<span class="plain">if (inferfrom ~= 0) {</span>
<span class="plain">for (j=inferfrom : j&lt;pcount : j++) {</span>
<span class="plain">if (pattern--&gt;j == PATTERN_NULL) continue;</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">i = 2+buffer-&gt;1; (buffer-&gt;1)++; buffer-&gt;(i++) = ' ';</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">i = WORDSIZE + buffer--&gt;0;</span>
<span class="plain">(buffer--&gt;0)++; buffer-&gt;(i++) = ' ';</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 5)</span>
<span class="plain">print "[Gluing in inference with pattern code ", pattern--&gt;j, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">! Conveniently, parse2--&gt;1 is the first word in both ZCODE and GLULX.</span>
<span class="plain">parse2--&gt;1 = 0;</span>
<span class="plain">! An inferred object. Best we can do is glue in a pronoun.</span>
<span class="plain">! (This is imperfect, but it's very seldom needed anyway.)</span>
<span class="plain">if (pattern--&gt;j &gt;= 2 &amp;&amp; pattern--&gt;j &lt; REPARSE_CODE) {</span>
<span class="plain">PronounNotice(pattern--&gt;j);</span>
<span class="plain">for (k=1 : k&lt;=LanguagePronouns--&gt;0 : k=k+3)</span>
<span class="plain">if (pattern--&gt;j == LanguagePronouns--&gt;(k+2)) {</span>
<span class="plain">parse2--&gt;1 = LanguagePronouns--&gt;k;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 5)</span>
<span class="plain">print "[Using pronoun '", (address) parse2--&gt;1, "']^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">break;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">else {</span>
<span class="plain">! An inferred preposition.</span>
<span class="plain">parse2--&gt;1 = VM_NumberToDictionaryAddress(pattern--&gt;j - REPARSE_CODE);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 5)</span>
<span class="plain">print "[Using preposition '", (address) parse2--&gt;1, "']^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">! parse2--&gt;1 now holds the dictionary address of the word to glue in.</span>
<span class="plain">if (parse2--&gt;1 ~= 0) {</span>
<span class="plain">k = buffer + i;</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">@output_stream 3 k;</span>
<span class="plain">print (address) parse2--&gt;1;</span>
<span class="plain">@output_stream -3;</span>
<span class="plain">k = k--&gt;0;</span>
<span class="plain">for (l=i : l&lt;i+k : l++) buffer-&gt;l = buffer-&gt;(l+2);</span>
<span class="plain">i = i + k; buffer-&gt;1 = i-2;</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">k = Glulx_PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2--&gt;1);</span>
<span class="plain">i = i + k; buffer--&gt;0 = i - WORDSIZE;</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! (2) we must glue the newly-typed text onto the end.</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">i = 2+buffer-&gt;1; (buffer-&gt;1)++; buffer-&gt;(i++) = ' ';</span>
<span class="plain">for (j=0 : j&lt;buffer2-&gt;1 : i++,j++) {</span>
<span class="plain">buffer-&gt;i = buffer2-&gt;(j+2);</span>
<span class="plain">(buffer-&gt;1)++;</span>
<span class="plain">if (buffer-&gt;1 == INPUT_BUFFER_LEN) break;</span>
<span class="plain">}</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">i = WORDSIZE + buffer--&gt;0;</span>
<span class="plain">(buffer--&gt;0)++; buffer-&gt;(i++) = ' ';</span>
<span class="plain">for (j=0 : j&lt;buffer2--&gt;0 : i++,j++) {</span>
<span class="plain">buffer-&gt;i = buffer2-&gt;(j+WORDSIZE);</span>
<span class="plain">(buffer--&gt;0)++;</span>
<span class="plain">if (buffer--&gt;0 == INPUT_BUFFER_LEN) break;</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">! (3) we fill up the buffer with spaces, which is unnecessary, but may</span>
<span class="plain">! help incorrectly-written interpreters to cope.</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">for (: i&lt;INPUT_BUFFER_LEN : i++) buffer-&gt;i = ' ';</span>
<span class="plain">#Endif; ! TARGET_ZCODE</span>
<span class="plain">jump RECONSTRUCT_INPUT;</span>
<span class="plain">]; ! end of NounDomain</span>
<span class="plain">[ PARSER_CLARIF_INTERNAL_R; ];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP35"></a><b>&#167;35. Adjudicate. </b>The <code class="display"><span class="extract">Adjudicate</span></code> routine tries to see if there is an obvious choice, when
faced with a list of objects (the <code class="display"><span class="extract">match_list</span></code>) each of which matches the
player's specification equally well. To do this it makes use of the <code class="display"><span class="extract">context</span></code>
(the token type being worked on).
</p>
<p class="inwebparagraph">It counts up the number of obvious choices for the given context &mdash; all to
do with where a candidate is, except for 6 (<code class="display"><span class="extract">animate</span></code>) which is to
do with whether it is animate or not &mdash; and then:
</p>
<p class="inwebparagraph"></p>
<ul class="items"><li>(a) if only one obvious choice is found, that is returned;
</li><li>(b) if we are in indefinite mode (don't care which) one of the obvious choices
is returned, or if there is no obvious choice then an unobvious one is made;
</li><li>(c) at this stage, we work out whether the objects are distinguishable from
each other or not: if they are all indistinguishable from each other, then
choose one, it doesn't matter which;
</li><li>(d) otherwise, 0 (meaning, unable to decide) is returned (but remember
that the equivalence classes we've just worked out will be needed by other
routines to clear up this mess, so we can't economise on working them out).
</li></ul>
<p class="inwebparagraph"><code class="display"><span class="extract">Adjudicate</span></code> returns -1 if an error occurred.
</p>
<pre class="display">
<span class="plain">[ Adjudicate context i j k good_ones last n ultimate flag offset;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) {</span>
<span class="plain">print " [Adjudicating match list of size ", number_matched,</span>
<span class="plain">" in context ", context, "^";</span>
<span class="plain">print " ";</span>
<span class="plain">if (indef_mode) {</span>
<span class="plain">print "indefinite type: ";</span>
<span class="plain">if (indef_type &amp; OTHER_BIT) print "other ";</span>
<span class="plain">if (indef_type &amp; MY_BIT) print "my ";</span>
<span class="plain">if (indef_type &amp; THAT_BIT) print "that ";</span>
<span class="plain">if (indef_type &amp; PLURAL_BIT) print "plural ";</span>
<span class="plain">if (indef_type &amp; LIT_BIT) print "lit ";</span>
<span class="plain">if (indef_type &amp; UNLIT_BIT) print "unlit ";</span>
<span class="plain">if (indef_owner ~= 0) print "owner:", (name) indef_owner;</span>
<span class="plain">new_line;</span>
<span class="plain">print " number wanted: ";</span>
<span class="plain">if (indef_wanted == INDEF_ALL_WANTED) print "all"; else print indef_wanted;</span>
<span class="plain">new_line;</span>
<span class="plain">print " most likely GNAs of names: ", indef_cases, "^";</span>
<span class="plain">}</span>
<span class="plain">else print "definite object^";</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">j = number_matched-1; good_ones = 0; last = match_list--&gt;0;</span>
<span class="plain">for (i=0 : i&lt;=j : i++) {</span>
<span class="plain">n = match_list--&gt;i;</span>
<span class="plain">match_scores--&gt;i = good_ones;</span>
<span class="plain">ultimate = ScopeCeiling(n);</span>
<span class="plain">if (context==HELD_TOKEN &amp;&amp; parent(n)==actor)</span>
<span class="plain">{ good_ones++; last=n; }</span>
<span class="plain">if (context==MULTI_TOKEN &amp;&amp; ultimate==ScopeCeiling(actor)</span>
<span class="plain">&amp;&amp; n~=actor &amp;&amp; n hasnt concealed &amp;&amp; n hasnt scenery)</span>
<span class="plain">{ good_ones++; last=n; }</span>
<span class="plain">if (context==MULTIHELD_TOKEN &amp;&amp; parent(n)==actor)</span>
<span class="plain">{ good_ones++; last=n; }</span>
<span class="plain">if (context==MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)</span>
<span class="plain">{ if (advance_warning==-1)</span>
<span class="plain">{ if (context==MULTIEXCEPT_TOKEN)</span>
<span class="plain">{ good_ones++; last=n;</span>
<span class="plain">}</span>
<span class="plain">if (context==MULTIINSIDE_TOKEN)</span>
<span class="plain">{ if (parent(n)~=actor) { good_ones++; last=n; }</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">else</span>
<span class="plain">{ if (context==MULTIEXCEPT_TOKEN &amp;&amp; n~=advance_warning)</span>
<span class="plain">{ good_ones++; last=n; }</span>
<span class="plain">if (context==MULTIINSIDE_TOKEN &amp;&amp; n in advance_warning)</span>
<span class="plain">{ good_ones++; last=n; }</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (context==CREATURE_TOKEN &amp;&amp; CreatureTest(n)==1)</span>
<span class="plain">{ good_ones++; last=n; }</span>
<span class="plain">match_scores--&gt;i = 1000*(good_ones - match_scores--&gt;i);</span>
<span class="plain">}</span>
<span class="plain">if (good_ones == 1) {</span>
<span class="plain">if (indef_mode == 1 &amp;&amp; indef_type &amp; PLURAL_BIT ~= 0 &amp;&amp;</span>
<span class="plain">context == MULTI_TOKEN or MULTIHELD_TOKEN or</span>
<span class="plain">MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {</span>
<span class="plain">BeginActivity(DECIDING_WHETHER_ALL_INC_ACT, last);</span>
<span class="plain">if ((ForActivity(DECIDING_WHETHER_ALL_INC_ACT, last)) &amp;&amp;</span>
<span class="plain">(RulebookFailed())) good_ones = 0;</span>
<span class="plain">EndActivity(DECIDING_WHETHER_ALL_INC_ACT, last);</span>
<span class="plain">if (good_ones == 1) return last;</span>
<span class="plain">} else {</span>
<span class="plain">return last;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! If there is ambiguity about what was typed, but it definitely wasn't</span>
<span class="plain">! animate as required, then return anything; higher up in the parser</span>
<span class="plain">! a suitable error will be given. (This prevents a question being asked.)</span>
<span class="plain">if (context == CREATURE_TOKEN &amp;&amp; good_ones == 0) return match_list--&gt;0;</span>
<span class="plain">if (indef_mode == 0) indef_type=0;</span>
<span class="plain">ScoreMatchL(context);</span>
<span class="plain">if (number_matched == 0) return -1;</span>
<span class="plain">if (indef_mode == 0) {</span>
<span class="plain">! Is there now a single highest-scoring object?</span>
<span class="plain">i = SingleBestGuess();</span>
<span class="plain">if (i &gt;= 0) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " Single best-scoring object returned.]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">return i;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (indef_mode == 1 &amp;&amp; indef_type &amp; PLURAL_BIT ~= 0) {</span>
<span class="plain">if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN</span>
<span class="plain">or MULTIINSIDE_TOKEN) {</span>
<span class="plain">etype = MULTI_PE;</span>
<span class="plain">return -1;</span>
<span class="plain">}</span>
<span class="plain">i = 0; offset = multiple_object--&gt;0;</span>
<span class="plain">for (j=BestGuess(): j~=-1 &amp;&amp; i&lt;indef_wanted &amp;&amp; i+offset&lt;MATCH_LIST_WORDS-1:</span>
<span class="plain">j=BestGuess()) {</span>
<span class="plain">flag = 0;</span>
<span class="plain">BeginActivity(DECIDING_WHETHER_ALL_INC_ACT, j);</span>
<span class="plain">if ((ForActivity(DECIDING_WHETHER_ALL_INC_ACT, j)) == 0) {</span>
<span class="plain">if (j hasnt concealed &amp;&amp; j hasnt worn) flag = 1;</span>
<span class="plain">if (context == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN &amp;&amp; parent(j) ~= actor)</span>
<span class="plain">flag = 0;</span>
<span class="plain">if (action_to_be == ##Take or ##Remove &amp;&amp; parent(j) == actor)</span>
<span class="plain">flag = 0;</span>
<span class="plain">k = ChooseObjects(j, flag);</span>
<span class="plain">if (k == 1)</span>
<span class="plain">flag = 1;</span>
<span class="plain">else {</span>
<span class="plain">if (k == 2) flag = 0;</span>
<span class="plain">}</span>
<span class="plain">} else {</span>
<span class="plain">flag = 0; if (RulebookSucceeded()) flag = 1;</span>
<span class="plain">}</span>
<span class="plain">EndActivity(DECIDING_WHETHER_ALL_INC_ACT, j);</span>
<span class="plain">if (flag == 1) {</span>
<span class="plain">i++; multiple_object--&gt;(i+offset) = j;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " Accepting it^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">else {</span>
<span class="plain">i = i;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " Rejecting it^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (i &lt; indef_wanted &amp;&amp; indef_wanted &lt; INDEF_ALL_WANTED) {</span>
<span class="plain">etype = TOOFEW_PE; multi_wanted = indef_wanted;</span>
<span class="plain">if (parser_trace &gt;= 4) print "Too few found^";</span>
<span class="plain">multi_had=i;</span>
<span class="plain">return -1;</span>
<span class="plain">}</span>
<span class="plain">multiple_object--&gt;0 = i+offset;</span>
<span class="plain">multi_context = context;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4)</span>
<span class="plain">print " Made multiple object of size ", i, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">return 1;</span>
<span class="plain">}</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++) match_classes--&gt;i = 0;</span>
<span class="plain">n = 1;</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++)</span>
<span class="plain">if (match_classes--&gt;i == 0) {</span>
<span class="plain">match_classes--&gt;i = n++; flag = 0;</span>
<span class="plain">for (j=i+1 : j&lt;number_matched : j++)</span>
<span class="plain">if (match_classes--&gt;j == 0 &amp;&amp; Identical(match_list--&gt;i, match_list--&gt;j) == 1) {</span>
<span class="plain">flag=1;</span>
<span class="plain">match_classes--&gt;j = match_classes--&gt;i;</span>
<span class="plain">}</span>
<span class="plain">if (flag == 1) match_classes--&gt;i = 1-n;</span>
<span class="plain">}</span>
<span class="plain">n--; number_of_classes = n;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) {</span>
<span class="plain">print " Grouped into ", n, " possibilities by name:^";</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++)</span>
<span class="plain">if (match_classes--&gt;i &gt; 0)</span>
<span class="plain">print " ", (The) match_list--&gt;i, " (", match_list--&gt;i, ") --- group ",</span>
<span class="plain">match_classes--&gt;i, "^";</span>
<span class="plain">}</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (indef_mode == 0) {</span>
<span class="plain">if (n &gt; 1) {</span>
<span class="plain">k = -1;</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++) {</span>
<span class="plain">if (match_scores--&gt;i &gt; k) {</span>
<span class="plain">k = match_scores--&gt;i;</span>
<span class="plain">j = match_classes--&gt;i; j = j*j;</span>
<span class="plain">flag = 0;</span>
<span class="plain">}</span>
<span class="plain">else</span>
<span class="plain">if (match_scores--&gt;i == k) {</span>
<span class="plain">if ((match_classes--&gt;i) * (match_classes--&gt;i) ~= j)</span>
<span class="plain">flag = 1;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (flag) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " Unable to choose best group, so ask player.]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">return 0;</span>
<span class="plain">}</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " Best choices are all from the same group.^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! When the player is really vague, or there's a single collection of</span>
<span class="plain">! indistinguishable objects to choose from, choose the one the player</span>
<span class="plain">! most recently acquired, or if the player has none of them, then</span>
<span class="plain">! the one most recently put where it is.</span>
<span class="plain">if (n == 1) dont_infer = true;</span>
<span class="plain">return BestGuess();</span>
<span class="plain">]; ! Adjudicate</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP36"></a><b>&#167;36. ReviseMulti. </b><code class="display"><span class="extract">ReviseMulti</span></code> revises the multiple object which already exists, in the
light of information which has come along since then (i.e., the second
parameter). It returns a parser error number, or else 0 if all is well.
This only ever throws things out, never adds new ones.
</p>
<pre class="display">
<span class="plain">[ ReviseMulti second_p i low;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4)</span>
<span class="plain">print " Revising multiple object list of size ", multiple_object--&gt;0,</span>
<span class="plain">" with 2nd ", (name) second_p, "^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (multi_context == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {</span>
<span class="plain">for (i=1,low=0 : i&lt;=multiple_object--&gt;0 : i++) {</span>
<span class="plain">if ( (multi_context==MULTIEXCEPT_TOKEN &amp;&amp; multiple_object--&gt;i ~= second_p) ||</span>
<span class="plain">(multi_context==MULTIINSIDE_TOKEN &amp;&amp; multiple_object--&gt;i in second_p)) {</span>
<span class="plain">low++;</span>
<span class="plain">multiple_object--&gt;low = multiple_object--&gt;i;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">multiple_object--&gt;0 = low;</span>
<span class="plain">}</span>
<span class="plain">if (multi_context == MULTI_TOKEN &amp;&amp; action_to_be == ##Take) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " Token 2 plural case: number with actor ", low, "^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (take_all_rule == 2) {</span>
<span class="plain">for (i=1,low=0 : i&lt;=multiple_object--&gt;0 : i++) {</span>
<span class="plain">if (ScopeCeiling(multiple_object--&gt;i) == ScopeCeiling(actor)) {</span>
<span class="plain">low++;</span>
<span class="plain">multiple_object--&gt;low = multiple_object--&gt;i;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">multiple_object--&gt;0 = low;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">i = multiple_object--&gt;0;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " Done: new size ", i, "^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (i == 0) return NOTHING_PE;</span>
<span class="plain">return 0;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP37"></a><b>&#167;37. Match List. </b>The match list is an array, <code class="display"><span class="extract">match_list--&gt;</span></code>, which holds the current best
guesses at what object(s) a portion of the command refers to. The global
<code class="display"><span class="extract">number_matched</span></code> is set to the current length of the <code class="display"><span class="extract">match_list</span></code>.
</p>
<p class="inwebparagraph">When the parser sees a possible match of object <code class="display"><span class="extract">obj</span></code> at quality level <code class="display"><span class="extract">q</span></code>,
it calls <code class="display"><span class="extract">MakeMatch(obj, q)</span></code>. If this is the best quality match so far, then
we wipe out all the previous matches and start a new list with this one.
If it's only as good as the best so far, we add it to the list (provided
we haven't run out of space, and provided it isn't in the list already).
If it's worse, we ignore it altogether.
</p>
<p class="inwebparagraph">I6 tokens in the form <code class="display"><span class="extract">noun=Filter</span></code> or <code class="display"><span class="extract">Attribute</span></code> are "noun filter tokens",
and mean that the match list should be filtered to accept only nouns which
are acceptable to the given routine, or have the given attribute. Such a
token is in force if <code class="display"><span class="extract">token_filter</span></code> is used. (I7 makes no use of this in the
attribute case, which is deprecated nowadays.)
</p>
<p class="inwebparagraph">Quality is essentially the number of words in the command referring to
the object: the idea is that "red panic button" is better than "red button"
or "panic".
</p>
<pre class="display">
<span class="plain">[ MakeMatch obj quality i;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 6) print " Match with quality ",quality,"^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (token_filter ~= 0 &amp;&amp; ConsultNounFilterToken(obj) == 0) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 6) print " Match filtered out: token filter ", token_filter, "^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">rtrue;</span>
<span class="plain">}</span>
<span class="plain">if (quality &lt; match_length) rtrue;</span>
<span class="plain">if (quality &gt; match_length) { match_length = quality; number_matched = 0; }</span>
<span class="plain">else {</span>
<span class="plain">if (number_matched &gt;= MATCH_LIST_WORDS) rtrue;</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++)</span>
<span class="plain">if (match_list--&gt;i == obj) rtrue;</span>
<span class="plain">}</span>
<span class="plain">match_list--&gt;number_matched++ = obj;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 6) print " Match added to list^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">];</span>
<span class="plain">[ ConsultNounFilterToken obj sn rv;</span>
<span class="plain">if (token_filter ofclass Routine) {</span>
<span class="plain">sn = noun;</span>
<span class="plain">noun = obj;</span>
<span class="plain">rv = indirect(token_filter);</span>
<span class="plain">noun = sn;</span>
<span class="plain">return rv;</span>
<span class="plain">}</span>
<span class="plain">if (obj has (token_filter-1)) rtrue;</span>
<span class="plain">rfalse;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP38"></a><b>&#167;38. ScoreMatchL. </b><code class="display"><span class="extract">ScoreMatchL</span></code> scores the match list for quality in terms of what the player
has vaguely asked for. Points are awarded for conforming with requirements
like "my", and so on. Remove from the match list any entries which fail
the basic requirements of the descriptors. (The scoring system used to
evaluate the possibilities is discussed in detail in the DM4.)
</p>
<pre class="display">
<span class="plain">Constant SCORE__CHOOSEOBJ = 1000;</span>
<span class="plain">Constant SCORE__IFGOOD = 500;</span>
<span class="plain">Constant SCORE__UNCONCEALED = 100;</span>
<span class="plain">Constant SCORE__BESTLOC = 60;</span>
<span class="plain">Constant SCORE__NEXTBESTLOC = 40;</span>
<span class="plain">Constant SCORE__NOTCOMPASS = 20;</span>
<span class="plain">Constant SCORE__NOTSCENERY = 10;</span>
<span class="plain">Constant SCORE__NOTACTOR = 5;</span>
<span class="plain">Constant SCORE__GNA = 1;</span>
<span class="plain">Constant SCORE__DIVISOR = 20;</span>
<span class="plain">Constant PREFER_HELD;</span>
<span class="plain">[ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;</span>
<span class="plain">! if (indef_type &amp; OTHER_BIT ~= 0) threshold++;</span>
<span class="plain">if (indef_type &amp; MY_BIT ~= 0) threshold++;</span>
<span class="plain">if (indef_type &amp; THAT_BIT ~= 0) threshold++;</span>
<span class="plain">if (indef_type &amp; LIT_BIT ~= 0) threshold++;</span>
<span class="plain">if (indef_type &amp; UNLIT_BIT ~= 0) threshold++;</span>
<span class="plain">if (indef_owner ~= nothing) threshold++;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " Scoring match list: indef mode ", indef_mode, " type ",</span>
<span class="plain">indef_type, ", satisfying ", threshold, " requirements:^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">#ifdef PREFER_HELD;</span>
<span class="plain">a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;</span>
<span class="plain">if (action_to_be == ##Take or ##Remove) {</span>
<span class="plain">a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC;</span>
<span class="plain">}</span>
<span class="plain">context = context; ! silence warning</span>
<span class="plain">#ifnot;</span>
<span class="plain">a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC;</span>
<span class="plain">if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {</span>
<span class="plain">a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;</span>
<span class="plain">}</span>
<span class="plain">#endif; ! PREFER_HELD</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++) {</span>
<span class="plain">obj = match_list--&gt;i; its_owner = parent(obj); its_score=0; met=0;</span>
<span class="plain">! if (indef_type &amp; OTHER_BIT ~= 0</span>
<span class="plain">! &amp;&amp; obj ~= itobj or himobj or herobj) met++;</span>
<span class="plain">if (indef_type &amp; MY_BIT ~= 0 &amp;&amp; its_owner == actor) met++;</span>
<span class="plain">if (indef_type &amp; THAT_BIT ~= 0 &amp;&amp; its_owner == actors_location) met++;</span>
<span class="plain">if (indef_type &amp; LIT_BIT ~= 0 &amp;&amp; obj has light) met++;</span>
<span class="plain">if (indef_type &amp; UNLIT_BIT ~= 0 &amp;&amp; obj hasnt light) met++;</span>
<span class="plain">if (indef_owner ~= 0 &amp;&amp; its_owner == indef_owner) met++;</span>
<span class="plain">if (met &lt; threshold) {</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4)</span>
<span class="plain">print " ", (The) match_list--&gt;i, " (", match_list--&gt;i, ") in ",</span>
<span class="plain">(the) its_owner, " is rejected (doesn't match descriptors)^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">match_list--&gt;i = -1;</span>
<span class="plain">}</span>
<span class="plain">else {</span>
<span class="plain">its_score = 0;</span>
<span class="plain">if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;</span>
<span class="plain">if (its_owner == actor) its_score = its_score + a_s;</span>
<span class="plain">else</span>
<span class="plain">if (its_owner == actors_location) its_score = its_score + l_s;</span>
<span class="plain">else</span>
<span class="plain">if (its_owner ~= Compass) its_score = its_score + SCORE__NOTCOMPASS;</span>
<span class="plain">its_score = its_score + SCORE__CHOOSEOBJ * ChooseObjects(obj, 2);</span>
<span class="plain">if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;</span>
<span class="plain">if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;</span>
<span class="plain">! A small bonus for having the correct GNA,</span>
<span class="plain">! for sorting out ambiguous articles and the like.</span>
<span class="plain">if (indef_cases &amp; (PowersOfTwo_TB--&gt;(GetGNAOfObject(obj))))</span>
<span class="plain">its_score = its_score + SCORE__GNA;</span>
<span class="plain">match_scores--&gt;i = match_scores--&gt;i + its_score;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4) print " ", (The) match_list--&gt;i, " (", match_list--&gt;i,</span>
<span class="plain">") in ", (the) its_owner, " : ", match_scores--&gt;i, " points^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++) {</span>
<span class="plain">while (match_list--&gt;i == -1) {</span>
<span class="plain">if (i == number_matched-1) { number_matched--; break; }</span>
<span class="plain">for (j=i : j&lt;number_matched-1 : j++) {</span>
<span class="plain">match_list--&gt;j = match_list--&gt;(j+1);</span>
<span class="plain">match_scores--&gt;j = match_scores--&gt;(j+1);</span>
<span class="plain">}</span>
<span class="plain">number_matched--;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP39"></a><b>&#167;39. BestGuess. </b><code class="display"><span class="extract">BestGuess</span></code> makes the best guess it can out of the match list, assuming that
everything in the match list is textually as good as everything else;
however it ignores items marked as -1, and so marks anything it chooses.
It returns -1 if there are no possible choices.
</p>
<pre class="display">
<span class="plain">[ BestGuess earliest its_score best i;</span>
<span class="plain">earliest = 0; best = -1;</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++) {</span>
<span class="plain">if (match_list--&gt;i &gt;= 0) {</span>
<span class="plain">its_score = match_scores--&gt;i;</span>
<span class="plain">if (its_score &gt; best) { best = its_score; earliest = i; }</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 4)</span>
<span class="plain">if (best &lt; 0) print " Best guess ran out of choices^";</span>
<span class="plain">else print " Best guess ", (the) match_list--&gt;earliest,</span>
<span class="plain">" (", match_list--&gt;earliest, ")^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (best &lt; 0) return -1;</span>
<span class="plain">i = match_list--&gt;earliest;</span>
<span class="plain">match_list--&gt;earliest = -1;</span>
<span class="plain">return i;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP40"></a><b>&#167;40. SingleBestGuess. </b><code class="display"><span class="extract">SingleBestGuess</span></code> returns the highest-scoring object in the match list
if it is the clear winner, or returns -1 if there is no clear winner.
</p>
<pre class="display">
<span class="plain">[ SingleBestGuess earliest its_score best i;</span>
<span class="plain">earliest = -1; best = -1000;</span>
<span class="plain">for (i=0 : i&lt;number_matched : i++) {</span>
<span class="plain">its_score = match_scores--&gt;i;</span>
<span class="plain">if (its_score == best) earliest = -1;</span>
<span class="plain">if (its_score &gt; best) { best = its_score; earliest = match_list--&gt;i; }</span>
<span class="plain">}</span>
<span class="plain">return earliest;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP41"></a><b>&#167;41. Identical. </b><code class="display"><span class="extract">Identical</span></code> decides whether or not two objects can be distinguished from each
other by anything the player can type. If not, it returns <code class="display"><span class="extract">true</span></code>. (This
routine is critical to the handling of plurals, and the list-writer
requires it to be an equivalence relation between objects: but it is,
because it is equivalent to O_1~ O_2 if and only if f(O_1) = f(O_2)
for some function f.)
</p>
<pre class="display">
<span class="plain">[ Identical o1 o2 p1 p2 n1 n2 i j flag;</span>
<span class="plain">if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side</span>
<span class="plain">if (o1 == 0 || o2 == 0) rfalse; ! Similarly</span>
<span class="plain">if (o1 ofclass K3_direction || o2 ofclass K3_direction) rfalse; ! Saves time</span>
<span class="plain">! What complicates things is that o1 or o2 might have a parsing routine,</span>
<span class="plain">! so the parser can't know from here whether they are or aren't the same.</span>
<span class="plain">! If they have different parsing routines, we simply assume they're</span>
<span class="plain">! different. If they have the same routine (which they probably got from</span>
<span class="plain">! a class definition) then the decision process is as follows:</span>
<span class="plain">!</span>
<span class="plain">! the routine is called (with self being o1, not that it matters)</span>
<span class="plain">! with noun and second being set to o1 and o2, and action being set</span>
<span class="plain">! to the fake action TheSame. If it returns -1, they are found</span>
<span class="plain">! identical; if -2, different; and if &gt;=0, then the usual method</span>
<span class="plain">! is used instead.</span>
<span class="plain">if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {</span>
<span class="plain">if (o1.parse_name ~= o2.parse_name) rfalse;</span>
<span class="plain">parser_action = ##TheSame; parser_one = o1; parser_two = o2;</span>
<span class="plain">j = wn; i = RunRoutines(o1,parse_name); wn = j;</span>
<span class="plain">if (i == -1) rtrue;</span>
<span class="plain">if (i == -2) rfalse;</span>
<span class="plain">}</span>
<span class="plain">! This is the default algorithm: do they have the same words in their</span>
<span class="plain">! "name" (i.e. property no. 1) properties. (Note that the following allows</span>
<span class="plain">! for repeated words and words in different orders.)</span>
<span class="plain">p1 = o1.&amp;1; n1 = (o1.#1)/WORDSIZE;</span>
<span class="plain">p2 = o2.&amp;1; n2 = (o2.#1)/WORDSIZE;</span>
<span class="plain">! for (i=0 : i&lt;n1 : i++) { print (address) p1--&gt;i, " "; } new_line;</span>
<span class="plain">! for (i=0 : i&lt;n2 : i++) { print (address) p2--&gt;i, " "; } new_line;</span>
<span class="plain">for (i=0 : i&lt;n1 : i++) {</span>
<span class="plain">flag = 0;</span>
<span class="plain">for (j=0 : j&lt;n2 : j++)</span>
<span class="plain">if (p1--&gt;i == p2--&gt;j) flag = 1;</span>
<span class="plain">if (flag == 0) rfalse;</span>
<span class="plain">}</span>
<span class="plain">for (j=0 : j&lt;n2 : j++) {</span>
<span class="plain">flag = 0;</span>
<span class="plain">for (i=0 : i&lt;n1 : i++)</span>
<span class="plain">if (p1--&gt;i == p2--&gt;j) flag = 1;</span>
<span class="plain">if (flag == 0) rfalse;</span>
<span class="plain">}</span>
<span class="plain">! print "Which are identical!^";</span>
<span class="plain">rtrue;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP42"></a><b>&#167;42. Print Command. </b><code class="display"><span class="extract">PrintCommand</span></code> reconstructs the command as it presently reads, from the
pattern which has been built up.
</p>
<p class="inwebparagraph">If <code class="display"><span class="extract">from</span></code> is 0, it starts with the verb: then it goes through the pattern.
</p>
<p class="inwebparagraph">The other parameter is <code class="display"><span class="extract">emptyf</span></code> &mdash; a flag: if 0, it goes up to <code class="display"><span class="extract">pcount</span></code>:
if 1, it goes up to <code class="display"><span class="extract">pcount</span></code>-1.
</p>
<p class="inwebparagraph">Note that verbs and prepositions are printed out of the dictionary:
and that since the dictionary may only preserve the first six characters
of a word (in a V3 game), we have to hand-code the longer words needed.
At present, I7 doesn't do this, but it probably should.
</p>
<p class="inwebparagraph">(Recall that pattern entries are 0 for "multiple object", 1 for "special
word", 2 to <code class="display"><span class="extract">REPARSE_CODE-1</span></code> are object numbers and <code class="display"><span class="extract">REPARSE_CODE+n</span></code> means
the preposition <code class="display"><span class="extract">n</span></code>.)
</p>
<pre class="display">
<span class="plain">[ PrintInferredCommand from singleton_noun;</span>
<span class="plain">singleton_noun = false;</span>
<span class="plain">if ((from ~= 0) &amp;&amp; (from == pcount-1) &amp;&amp;</span>
<span class="plain">(pattern--&gt;from &gt; 1) &amp;&amp; (pattern--&gt;from &lt; REPARSE_CODE))</span>
<span class="plain">singleton_noun = true;</span>
<span class="plain">if (singleton_noun) {</span>
<span class="plain">BeginActivity(CLARIFYING_PARSERS_CHOICE_ACT, pattern--&gt;from);</span>
<span class="plain">if (ForActivity(CLARIFYING_PARSERS_CHOICE_ACT, pattern--&gt;from) == 0) {</span>
<span class="plain">print "("; PrintCommand(from); print ")^";</span>
<span class="plain">}</span>
<span class="plain">EndActivity(CLARIFYING_PARSERS_CHOICE_ACT, pattern--&gt;from);</span>
<span class="plain">} else {</span>
<span class="plain">print "("; PrintCommand(from); print ")^";</span>
<span class="plain">}</span>
<span class="plain">];</span>
<span class="plain">[ PrintCommand from i k spacing_flag;</span>
<span class="plain">if (from == 0) {</span>
<span class="plain">i = verb_word;</span>
<span class="plain">if (LanguageVerb(i) == 0)</span>
<span class="plain">if (PrintVerb(i) == 0) print (address) i;</span>
<span class="plain">from++; spacing_flag = true;</span>
<span class="plain">}</span>
<span class="plain">for (k=from : k&lt;pcount : k++) {</span>
<span class="plain">i = pattern--&gt;k;</span>
<span class="plain">if (i == PATTERN_NULL) continue;</span>
<span class="plain">if (spacing_flag) print (char) ' ';</span>
<span class="plain">if (i == 0) { PARSER_CLARIF_INTERNAL_RM('F'); jump TokenPrinted; }</span>
<span class="plain">if (i == 1) { PARSER_CLARIF_INTERNAL_RM('G'); jump TokenPrinted; }</span>
<span class="plain">if (i &gt;= REPARSE_CODE)</span>
<span class="plain">print (address) VM_NumberToDictionaryAddress(i-REPARSE_CODE);</span>
<span class="plain">else</span>
<span class="plain">if (i ofclass K3_direction)</span>
<span class="plain">print (LanguageDirection) i; ! the direction name as adverb</span>
<span class="plain">else</span>
<span class="plain">print (the) i;</span>
<span class="plain">.TokenPrinted;</span>
<span class="plain">spacing_flag = true;</span>
<span class="plain">}</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP43"></a><b>&#167;43. CantSee. </b>The <code class="display"><span class="extract">CantSee</span></code> routine returns a good error number for the situation where
the last word looked at didn't seem to refer to any object in context.
</p>
<p class="inwebparagraph">The idea is that: if the actor is in a location (but not inside something
like, for instance, a tank which is in that location) then an attempt to
refer to one of the words listed as meaningful-but-irrelevant there
will cause "you don't need to refer to that in this game" rather than
"no such thing" or "what's `it'?".
</p>
<p class="inwebparagraph">(The advantage of not having looked at "irrelevant" local nouns until
now is that it stops them from clogging up the ambiguity-resolving process.
Thus game objects always triumph over scenery.)
</p>
<pre class="display">
<span class="plain">[ CantSee i w e;</span>
<span class="plain">saved_oops=oops_from;</span>
<span class="plain">if (scope_token ~= 0) {</span>
<span class="plain">scope_error = scope_token; return ASKSCOPE_PE;</span>
<span class="plain">}</span>
<span class="plain">wn--; w = NextWord();</span>
<span class="plain">e = CANTSEE_PE;</span>
<span class="plain">if (w == pronoun_word) {</span>
<span class="plain">w = NextWordStopped(); wn--;</span>
<span class="plain">if ((w == -1) || (line_token--&gt;(pcount) ~= ENDIT_TOKEN)) {</span>
<span class="plain">if (pcount &gt; 0) AnalyseToken(line_token--&gt;(pcount-1));</span>
<span class="plain">if ((pcount &gt; 0) &amp;&amp; (found_ttype == ROUTINE_FILTER_TT or ATTR_FILTER_TT))</span>
<span class="plain">e = NOTINCONTEXT_PE;</span>
<span class="plain">else {</span>
<span class="plain">pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;</span>
<span class="plain">e = ITGONE_PE;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (etype &gt; e) return etype;</span>
<span class="plain">return e;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP44"></a><b>&#167;44. Multiple Object List. </b>The <code class="display"><span class="extract">MultiAdd</span></code> routine adds object <code class="display"><span class="extract">o</span></code> to the multiple-object-list. This is
only allowed to hold <code class="display"><span class="extract">MATCH_LIST_WORDS</span></code> minus one objects at most, at which
point it ignores any new entries (and sets a global flag so that a warning
may later be printed if need be).
</p>
<p class="inwebparagraph">The <code class="display"><span class="extract">MultiSub</span></code> routine deletes object <code class="display"><span class="extract">o</span></code> from the multiple-object-list.
It returns 0 if the object was there in the first place, and 9 (because
this is the appropriate error number in <code class="display"><span class="extract">Parser()</span></code>) if it wasn't.
</p>
<p class="inwebparagraph">The <code class="display"><span class="extract">MultiFilter</span></code> routine goes through the multiple-object-list and throws
out anything without the given attribute <code class="display"><span class="extract">attr</span></code> set.
</p>
<pre class="display">
<span class="plain">[ MultiAdd o i j;</span>
<span class="plain">i = multiple_object--&gt;0;</span>
<span class="plain">if (i == MATCH_LIST_WORDS-1) { toomany_flag = 1; rtrue; }</span>
<span class="plain">for (j=1 : j&lt;=i : j++)</span>
<span class="plain">if (o == multiple_object--&gt;j) rtrue;</span>
<span class="plain">i++;</span>
<span class="plain">multiple_object--&gt;i = o;</span>
<span class="plain">multiple_object--&gt;0 = i;</span>
<span class="plain">];</span>
<span class="plain">[ MultiSub o i j k;</span>
<span class="plain">i = multiple_object--&gt;0;</span>
<span class="plain">for (j=1 : j&lt;=i : j++)</span>
<span class="plain">if (o == multiple_object--&gt;j) {</span>
<span class="plain">for (k=j : k&lt;=i : k++) multiple_object--&gt;k = multiple_object--&gt;(k+1);</span>
<span class="plain">multiple_object--&gt;0 = --i;</span>
<span class="plain">return 0;</span>
<span class="plain">}</span>
<span class="plain">return VAGUE_PE;</span>
<span class="plain">];</span>
<span class="plain">[ MultiFilter attr i j o;</span>
<span class="plain">.MFiltl;</span>
<span class="plain">i = multiple_object--&gt;0;</span>
<span class="plain">for (j=1 : j&lt;=i : j++) {</span>
<span class="plain">o = multiple_object--&gt;j;</span>
<span class="plain">if (o hasnt attr) { MultiSub(o); jump MFiltl; }</span>
<span class="plain">}</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP45"></a><b>&#167;45. Scope. </b>The scope of an actor is the set of objects which he can refer to in typed
commands, which is normally the same as the set of visible objects; but this
can be modified. This is how I7 handles tokens like "[any room]".
</p>
<p class="inwebparagraph">Scope determination is done by calling <code class="display"><span class="extract">SearchScope</span></code> to iterate through the
objects in scope, and "visit" each one: which means, carry out some task
for each as we get there. The task depends on the current value of
<code class="display"><span class="extract">scope_reason</span></code>, which is <code class="display"><span class="extract">PARSING_REASON</span></code> when the parser is matching
command text against object names.
</p>
<p class="inwebparagraph">The scope machinery is built on a number of levels, each making use only
of lower levels:
</p>
<ul class="items"><li>(0) Either <code class="display"><span class="extract">NounDomain</span></code>, <code class="display"><span class="extract">TestScope</span></code> or <code class="display"><span class="extract">LoopOverScope</span></code> makes one or more
calls to <code class="display"><span class="extract">SearchScope</span></code> (on level 1). The point of making multiple calls
is to influence the order in which items in scope are visited, which improves
the quality of "take all"-style multiple object lists, for instance.
</li><li>(1) <code class="display"><span class="extract">SearchScope</span></code> searches for the objects in scope which are within first
one domain, and then another: for instance, first within the current room
but not within the current actor, and then within the current actor. It can
be called either from level 0, or externally from the choose-objects
machinery, but is not recursive. It works within the context of a given
token in the parser (when called for <code class="display"><span class="extract">PARSING_REASON</span></code>) and in particular
the <code class="display"><span class="extract">multiinside</span></code> token, and also handles testing commands, scope tokens,
scope in darkness, and intervention by the I7 "deciding the scope of"
activity. Most of its actual searches are delegated to <code class="display"><span class="extract">ScopeWithin</span></code> (level
2), but it also uses <code class="display"><span class="extract">DoScopeActionAndRecurse</span></code> (level 3) and
<code class="display"><span class="extract">DoScopeAction</span></code> (level 4) as necessary.
</li><li>(2) <code class="display"><span class="extract">ScopeWithin</span></code> iterates through the objects in scope which are within
one supplied domain, but not within another. It can be called either
from level 1, or independently from rules in the "deciding the scope of"
activity via the I7 "place the contents of X in scope" phrase. It calls
<code class="display"><span class="extract">DoScopeActionAndRecurse</span></code> (level 3) on any unconcealed objects it finds.
</li><li>(3) <code class="display"><span class="extract">DoScopeActionAndRecurse</span></code> visits a given object by calling down to
<code class="display"><span class="extract">DoScopeAction</span></code> (level 4), and recurses to all unconcealed object-tree
contents and component parts of the object. The I7 phrase "place X in
scope" uses this routine.
</li><li>(4) <code class="display"><span class="extract">DoScopeAction</span></code> simply visits a single object, taking whatever action
is needed there &mdash; which will depend on the <code class="display"><span class="extract">scope_reason</span></code>. The only use
made by the parser of <code class="display"><span class="extract">TryGivenObject</span></code>, which tries to match command text
against the name of a given object, is from here. The I7 phrase "place X
in scope, but not its contents" uses this routine.
</li></ul>
<p class="inwebparagraph">Two routines are provided for code external to the parser to modify the
scope. They should be called only during scope deliberations &mdash; i.e.,
in <code class="display"><span class="extract">scope=...</span></code> tokens or in rules for the "deciding the scope of"
activity. (At present, <code class="display"><span class="extract">AddToScope</span></code> is not used in I7 at all.) Note
that this I7 form of <code class="display"><span class="extract">PlaceInScope</span></code> has a slightly different specification
to its I6 library counterpart of the same name: it can place a room in
scope. (In I6, room names were not normally parsed.)
</p>
<pre class="display">
<span class="plain">[ PlaceInScope O opts ws; ! If opts is set, do not place contents in scope</span>
<span class="plain">ws = wn; wn = match_from;</span>
<span class="plain">if (opts == false) DoScopeActionAndRecurse(O);</span>
<span class="plain">else DoScopeAction(O);</span>
<span class="plain">wn = ws; return;</span>
<span class="plain">];</span>
<span class="plain">[ AddToScope obj;</span>
<span class="plain">if (ats_flag &gt;= 2) DoScopeActionAndRecurse(obj, 0, ats_flag-2);</span>
<span class="plain">if (ats_flag == 1) { if (HasLightSource(obj)==1) ats_hls = 1; }</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP46"></a><b>&#167;46. Scope Level 0. </b>The two ways of starting up the scope machinery other than via the parser
code above.
</p>
<pre class="display">
<span class="plain">[ TestScope obj act a al sr x y;</span>
<span class="plain">x = parser_one; y = parser_two;</span>
<span class="plain">parser_one = obj; parser_two = 0; a = actor; al = actors_location;</span>
<span class="plain">sr = scope_reason; scope_reason = TESTSCOPE_REASON;</span>
<span class="plain">if (act == 0) actor = player; else actor = act;</span>
<span class="plain">actors_location = ScopeCeiling(actor);</span>
<span class="plain">SearchScope(actors_location, actor, 0); scope_reason = sr; actor = a;</span>
<span class="plain">actors_location = al; parser_one = x; x = parser_two; parser_two = y;</span>
<span class="plain">return x;</span>
<span class="plain">];</span>
<span class="plain">[ LoopOverScope routine act x y a al;</span>
<span class="plain">x = parser_one; y = scope_reason; a = actor; al = actors_location;</span>
<span class="plain">parser_one = routine;</span>
<span class="plain">if (act == 0) actor = player; else actor = act;</span>
<span class="plain">actors_location = ScopeCeiling(actor);</span>
<span class="plain">scope_reason = LOOPOVERSCOPE_REASON;</span>
<span class="plain">SearchScope(actors_location, actor, 0);</span>
<span class="plain">parser_one = x; scope_reason = y; actor = a; actors_location = al;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP47"></a><b>&#167;47. SearchScope. </b>Level 1. The method is:
</p>
<ul class="items"><li>(a) If the context is a <code class="display"><span class="extract">scope=...</span></code> token, then the search is delegated
to "stage 2" of the scope routine. This was the old I6 way to override
the searching behaviour: while users probably won't be using it any more,
the template does, in order to give testing commands universal scope which
is exempt from the activity below; and the NI compiler creates <code class="display"><span class="extract">scope=...</span></code>
tokens to handle Understand grammar such as "[any room]". So the feature
remains very much still in use.
</li><li>(b) The "deciding the scope of" activity is given the chance to intervene.
This is the I7 way to override the searching behaviour, and is the one taken
by users.
</li><li>(c) And otherwise:
<ul class="items"><li>(1) The I6 <code class="display"><span class="extract">multiinside</span></code> token, used as the first noun of its grammar line,
has as its scope all of the objects which are inside or on top of the
second noun of the grammar line. This provides a neat scope for the
ALL in a command like GET ALL FROM CUPBOARD, where the player clearly
does not intend ALL to refer to the cupboard itself, for instance. The
difficulty is that we don't yet know what the second object is, if we are
parsing left to right. But the parser code above has taken care of all of
that, and the <code class="display"><span class="extract">advance_warning</span></code> global is set to the object number of the
second noun, or to -1 if that is not yet known. Note that we check that
the contents are visible before adding them to scope, because otherwise
an unscrupulous player could use such a command to detect the contents of
an opaque locked box. If this rule applies, we skip (c.2), (c.3) and (c.4).
</li><li>(2) For all other tokens except <code class="display"><span class="extract">creature</span></code>, searching scope for the room
holding the current actor always catches the compass directions unless a
definite article has already been typed. (Thus OPEN THE EAST would match
an object called "east door", but not the compass direction "east".)
</li><li>(3) The contents of <code class="display"><span class="extract">domain1</span></code> which are not contents of <code class="display"><span class="extract">domain2</span></code> are
placed in scope, and so are any component parts of <code class="display"><span class="extract">domain1</span></code>. If <code class="display"><span class="extract">domain1</span></code>
is a container or supporter, it is placed in scope itself.
</li><li>(4) The contents and component parts of <code class="display"><span class="extract">domain2</span></code> are placed in scope.
If <code class="display"><span class="extract">domain2</span></code> is a container or supporter, it is placed in scope itself.
</li><li>(5) In darkness, the actor and his component parts are in scope. If the
actor is inside or on top of something, then that thing is also in scope.
(This avoids a situation where the player gets into an opaque box, then
pulls it closed from the inside, plunging himself into darkness, then types
OPEN BOX only to be told that he can't see any such thing.)
</li></ul>
</li></ul>
<pre class="display">
<span class="plain">[ SearchScope domain1 domain2 context i;</span>
<span class="plain">if (domain1 == 0) return;</span>
<span class="plain">! (a)</span>
<span class="plain">if (scope_token) {</span>
<span class="plain">scope_stage = 2;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 3) print " [Scope routine called at stage 2]^";</span>
<span class="plain">#Endif;</span>
<span class="plain">if (indirect(scope_token) ~= 0) rtrue;</span>
<span class="plain">}</span>
<span class="plain">! (b)</span>
<span class="plain">BeginActivity(DECIDING_SCOPE_ACT, actor);</span>
<span class="plain">if (ForActivity(DECIDING_SCOPE_ACT, actor) == false) {</span>
<span class="plain">! (c.1)</span>
<span class="plain">if ((scope_reason == PARSING_REASON) &amp;&amp; (context == MULTIINSIDE_TOKEN) &amp;&amp;</span>
<span class="plain">(advance_warning ~= -1)) {</span>
<span class="plain">if (IsSeeThrough(advance_warning) == 1)</span>
<span class="plain">ScopeWithin(advance_warning, 0, context);</span>
<span class="plain">} else {</span>
<span class="plain">! (c.2)</span>
<span class="plain">if ((scope_reason == PARSING_REASON) &amp;&amp; (context ~= CREATURE_TOKEN) &amp;&amp;</span>
<span class="plain">(indef_mode == 0) &amp;&amp; (domain1 == actors_location))</span>
<span class="plain">ScopeWithin(Compass);</span>
<span class="plain">! (c.3)</span>
<span class="plain">if (domain1 has supporter or container) DoScopeAction(domain1);</span>
<span class="plain">ScopeWithin(domain1, domain2, context);</span>
<span class="plain">! (c.4)</span>
<span class="plain">if (domain2) {</span>
<span class="plain">if (domain2 has supporter or container) DoScopeAction(domain2);</span>
<span class="plain">ScopeWithin(domain2, 0, context);</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! (c.5)</span>
<span class="plain">if (thedark == domain1 or domain2) {</span>
<span class="plain">DoScopeActionAndRecurse(actor, actor, context);</span>
<span class="plain">if (parent(actor) has supporter or container)</span>
<span class="plain">DoScopeActionAndRecurse(parent(actor), parent(actor), context);</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">EndActivity(DECIDING_SCOPE_ACT, actor);</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP48"></a><b>&#167;48. ScopeWithin. </b>Level 2. <code class="display"><span class="extract">ScopeWithin</span></code> puts objects visible from within the <code class="display"><span class="extract">domain</span></code> into scope.
An item belonging to the <code class="display"><span class="extract">domain</span></code> is placed in scope unless it is being
concealed by the <code class="display"><span class="extract">domain</span></code>: and even then, if the <code class="display"><span class="extract">domain</span></code> is the current
actor. Suppose Zorro conceals a book beneath his cloak: then the book is
not in scope to his lady friend The Black Whip, but it is in scope to Zorro
himself. (Thus an actor is not allowed to conceal anything from himself.)
</p>
<p class="inwebparagraph">Note that the <code class="display"><span class="extract">domain</span></code> object itself, and its component parts if any, are
not placed in scope by this routine, though nothing prevents some other
code doing so.
</p>
<pre class="display">
<span class="plain">[ ScopeWithin domain nosearch context obj next_obj;</span>
<span class="plain">if (domain == 0) rtrue;</span>
<span class="plain">! Look through the objects in the domain, avoiding "objectloop" in case</span>
<span class="plain">! movements occur.</span>
<span class="plain">obj = child(domain);</span>
<span class="plain">while (obj) {</span>
<span class="plain">next_obj = sibling(obj);</span>
<span class="plain">if ((domain == actor) || (TestConcealment(domain, obj) == false))</span>
<span class="plain">DoScopeActionAndRecurse(obj, nosearch, context);</span>
<span class="plain">obj = next_obj;</span>
<span class="plain">}</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP49"></a><b>&#167;49. DoScopeActionAndRecurse. </b>Level 3.
In all cases, the <code class="display"><span class="extract">domain</span></code> itself is visited. There are then three possible
forms of recursion:
</p>
<ul class="items"><li>(a) To unconcealed objects which are inside, on top of, carried or worn by
the <code class="display"><span class="extract">domain</span></code>: this is called "searching" in traditional I6 language and
is suppressed if <code class="display"><span class="extract">domain</span></code> is the special value <code class="display"><span class="extract">nosearch</span></code>.
</li><li>(b) To unconcealed component parts of the <code class="display"><span class="extract">domain</span></code>.
</li><li>(c) To any other objects listed in the <code class="display"><span class="extract">add_to_scope</span></code> property array, or
supplied by the <code class="display"><span class="extract">add_to_scope</span></code> property routine, if it has one. (I7 does
not usually use <code class="display"><span class="extract">add_to_scope</span></code>, but it remains a useful hook in the parser,
so it retains its old I6 library interpretation.)
</li></ul>
<pre class="display">
<span class="plain">[ DoScopeActionAndRecurse domain nosearch context i ad n obj next_obj;</span>
<span class="plain">DoScopeAction(domain);</span>
<span class="plain">! (a)</span>
<span class="plain">if ((domain ~= nosearch) &amp;&amp;</span>
<span class="plain">((domain ofclass K1_room or K8_person) || (IsSeeThrough(domain) == 1))) {</span>
<span class="plain">obj = child(domain);</span>
<span class="plain">while (obj) {</span>
<span class="plain">next_obj = sibling(obj);</span>
<span class="plain">if ((domain == actor) || (TestConcealment(domain, obj) == false))</span>
<span class="plain">DoScopeActionAndRecurse(obj, nosearch, context);</span>
<span class="plain">obj = next_obj;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! (b)</span>
<span class="plain">if (domain provides component_child) {</span>
<span class="plain">obj = domain.component_child;</span>
<span class="plain">while (obj) {</span>
<span class="plain">next_obj = obj.component_sibling;</span>
<span class="plain">if ((domain == actor) || (TestConcealment(domain, obj) == false))</span>
<span class="plain">DoScopeActionAndRecurse(obj, 0, context);</span>
<span class="plain">obj = next_obj;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">! (c)</span>
<span class="plain">ad = domain.&amp;add_to_scope;</span>
<span class="plain">if (ad ~= 0) {</span>
<span class="plain">! Test if the property value is not an object.</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">i = (UnsignedCompare(ad--&gt;0, top_object) &gt; 0);</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">i = (((ad--&gt;0)-&gt;0) ~= $70);</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">if (i) {</span>
<span class="plain">ats_flag = 2+context;</span>
<span class="plain">RunRoutines(domain, add_to_scope);</span>
<span class="plain">ats_flag = 0;</span>
<span class="plain">}</span>
<span class="plain">else {</span>
<span class="plain">n = domain.#add_to_scope;</span>
<span class="plain">for (i=0 : (WORDSIZE*i)&lt;n : i++)</span>
<span class="plain">if (ad--&gt;i)</span>
<span class="plain">DoScopeActionAndRecurse(ad--&gt;i, 0, context);</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP50"></a><b>&#167;50. DoScopeAction. </b>Level 4. This is where we take whatever action is to be performed as the
"visit" to each scoped object, and it's the bottom at last of the scope
mechanism.
</p>
<pre class="display">
<span class="plain">[ DoScopeAction item;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 6)</span>
<span class="plain">print "[DSA on ", (the) item, " with reason = ", scope_reason,</span>
<span class="plain">" p1 = ", parser_one, " p2 = ", parser_two, "]^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">@push parser_one; @push scope_reason;</span>
<span class="plain">switch(scope_reason) {</span>
<span class="plain">TESTSCOPE_REASON: if (item == parser_one) parser_two = 1;</span>
<span class="plain">LOOPOVERSCOPE_REASON: if (parser_one ofclass Routine) indirect(parser_one, item);</span>
<span class="plain">PARSING_REASON, TALKING_REASON: MatchTextAgainstObject(item);</span>
<span class="plain">}</span>
<span class="plain">@pull scope_reason; @pull parser_one;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP51"></a><b>&#167;51. Parsing Object Names. </b>We now reach the final major block of code in the parser: the part which tries
to match a given object's name(s) against the text at word position <code class="display"><span class="extract">match_from</span></code>
in the player's command, and calls <code class="display"><span class="extract">MakeMatch</span></code> if it succeeds. There are
basically four possibilities: ME, a pronoun such as IT, a name which doesn't
begin misleadingly with a number, and a name which does. In the latter two
cases, we pass the job down to <code class="display"><span class="extract">TryGivenObject</span></code>.
</p>
<pre class="display">
<span class="plain">[ MatchTextAgainstObject item i;</span>
<span class="plain">if (token_filter ~= 0 &amp;&amp; ConsultNounFilterToken(item) == 0) return;</span>
<span class="plain">if (match_from &lt;= num_words) { ! If there's any text to match, that is</span>
<span class="plain">wn = match_from;</span>
<span class="plain">i = NounWord();</span>
<span class="plain">if ((i == 1) &amp;&amp; (player == item)) MakeMatch(item, 1); ! "me"</span>
<span class="plain">if ((i &gt;= 2) &amp;&amp; (i &lt; 128) &amp;&amp; (LanguagePronouns--&gt;i == item)) MakeMatch(item, 1);</span>
<span class="plain">}</span>
<span class="plain">! Construing the current word as the start of a noun, can it refer to the</span>
<span class="plain">! object?</span>
<span class="plain">wn = match_from;</span>
<span class="plain">if (TryGivenObject(item) &gt; 0)</span>
<span class="plain">if (indef_nspec_at &gt; 0 &amp;&amp; match_from ~= indef_nspec_at) {</span>
<span class="plain">! This case arises if the player has typed a number in</span>
<span class="plain">! which is hypothetically an indefinite descriptor:</span>
<span class="plain">! e.g. "take two clubs". We have just checked the object</span>
<span class="plain">! against the word "clubs", in the hope of eventually finding</span>
<span class="plain">! two such objects. But we also backtrack and check it</span>
<span class="plain">! against the words "two clubs", in case it turns out to</span>
<span class="plain">! be the 2 of Clubs from a pack of cards, say. If it does</span>
<span class="plain">! match against "two clubs", we tear up our original</span>
<span class="plain">! assumption about the meaning of "two" and lapse back into</span>
<span class="plain">! definite mode.</span>
<span class="plain">wn = indef_nspec_at;</span>
<span class="plain">if (TryGivenObject(item) &gt; 0) {</span>
<span class="plain">match_from = indef_nspec_at;</span>
<span class="plain">ResetDescriptors();</span>
<span class="plain">}</span>
<span class="plain">wn = match_from;</span>
<span class="plain">}</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP52"></a><b>&#167;52. TryGivenObject. </b><code class="display"><span class="extract">TryGivenObject</span></code> tries to match as many words as possible in what has been
typed to the given object, <code class="display"><span class="extract">obj</span></code>. If it manages any words matched at all,
it calls <code class="display"><span class="extract">MakeMatch</span></code> to say so, then returns the number of words (or 1
if it was a match because of inadequate input).
</p>
<pre class="display">
<span class="plain">[ TryGivenObject obj nomatch threshold k w j;</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 5) print " Trying ", (the) obj, " (", obj, ") at word ", wn, "^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (nomatch &amp;&amp; obj == 0) return 0;</span>
<span class="plain">! if (nomatch) print "*** TryGivenObject *** on ", (the) obj, " at wn = ", wn, "^";</span>
<span class="plain">dict_flags_of_noun = 0;</span>
<span class="plain">! If input has run out then always match, with only quality 0 (this saves</span>
<span class="plain">! time).</span>
<span class="plain">if (wn &gt; num_words) {</span>
<span class="plain">if (nomatch) return 0;</span>
<span class="plain">if (indef_mode ~= 0)</span>
<span class="plain">dict_flags_of_noun = $$01110000; ! Reject "plural" bit</span>
<span class="plain">MakeMatch(obj,0);</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 5) print " Matched (0)^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">return 1;</span>
<span class="plain">}</span>
<span class="plain">! Ask the object to parse itself if necessary, sitting up and taking notice</span>
<span class="plain">! if it says the plural was used:</span>
<span class="plain">if (obj.parse_name~=0) {</span>
<span class="plain">parser_action = NULL; j=wn;</span>
<span class="plain">k = RunRoutines(obj,parse_name);</span>
<span class="plain">if (k &gt; 0) {</span>
<span class="plain">wn=j+k;</span>
<span class="plain">.MMbyPN;</span>
<span class="plain">if (parser_action == ##PluralFound)</span>
<span class="plain">dict_flags_of_noun = dict_flags_of_noun | 4;</span>
<span class="plain">if (dict_flags_of_noun &amp; 4) {</span>
<span class="plain">if (~~allow_plurals) k = 0;</span>
<span class="plain">else {</span>
<span class="plain">if (indef_mode == 0) {</span>
<span class="plain">indef_mode = 1; indef_type = 0; indef_wanted = 0;</span>
<span class="plain">}</span>
<span class="plain">indef_type = indef_type | PLURAL_BIT;</span>
<span class="plain">if (indef_wanted == 0) indef_wanted = INDEF_ALL_WANTED;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">#Ifdef DEBUG;</span>
<span class="plain">if (parser_trace &gt;= 5) print " Matched (", k, ")^";</span>
<span class="plain">#Endif; ! DEBUG</span>
<span class="plain">if (nomatch == false) MakeMatch(obj,k);</span>
<span class="plain">return k;</span>
<span class="plain">}</span>
<span class="plain">if (k == 0) jump NoWordsMatch;</span>
<span class="plain">}</span>
<span class="plain">! The default algorithm is simply to count up how many words pass the</span>
<span class="plain">! Refers test:</span>
<span class="plain">parser_action = NULL;</span>
<span class="plain">w = NounWord();</span>
<span class="plain">if (w == 1 &amp;&amp; player == obj) { k=1; jump MMbyPN; }</span>
<span class="plain">if (w &gt;= 2 &amp;&amp; w &lt; 128 &amp;&amp; (LanguagePronouns--&gt;w == obj)) { k = 1; jump MMbyPN; }</span>
<span class="plain">if (Refers(obj, wn-1) == 0) {</span>
<span class="plain">.NoWordsMatch;</span>
<span class="plain">if (indef_mode ~= 0) { k = 0; parser_action = NULL; jump MMbyPN; }</span>
<span class="plain">rfalse;</span>
<span class="plain">}</span>
<span class="plain">threshold = 1;</span>
<span class="plain">dict_flags_of_noun = (w-&gt;#dict_par1) &amp; $$01110100;</span>
<span class="plain">w = NextWord();</span>
<span class="plain">while (Refers(obj, wn-1)) {</span>
<span class="plain">threshold++;</span>
<span class="plain">if (w)</span>
<span class="plain">dict_flags_of_noun = dict_flags_of_noun | ((w-&gt;#dict_par1) &amp; $$01110100);</span>
<span class="plain">w = NextWord();</span>
<span class="plain">}</span>
<span class="plain">k = threshold;</span>
<span class="plain">jump MMbyPN;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP53"></a><b>&#167;53. Refers. </b><code class="display"><span class="extract">Refers</span></code> works out whether the word at number wnum can refer to the object
<code class="display"><span class="extract">obj</span></code>, returning true or false. The standard method is to see if the word
is listed under the <code class="display"><span class="extract">name</span></code> property for the object, but this is more
complex in languages other than English.
</p>
<pre class="display">
<span class="plain">[ Refers obj wnum wd k l m;</span>
<span class="plain">if (obj == 0) rfalse;</span>
<span class="plain">#Ifdef LanguageRefers;</span>
<span class="plain">k = LanguageRefers(obj,wnum); if (k &gt;= 0) return k;</span>
<span class="plain">#Endif; ! LanguageRefers</span>
<span class="plain">k = wn; wn = wnum; wd = NextWordStopped(); wn = k;</span>
<span class="plain">if (parser_inflection &gt;= 256) {</span>
<span class="plain">k = indirect(parser_inflection, obj, wd);</span>
<span class="plain">if (k &gt;= 0) return k;</span>
<span class="plain">m = -k;</span>
<span class="plain">}</span>
<span class="plain">else</span>
<span class="plain">m = parser_inflection;</span>
<span class="plain">k = obj.&amp;m; l = (obj.#m)/WORDSIZE-1;</span>
<span class="plain">for (m=0 : m&lt;=l : m++)</span>
<span class="plain">if (wd == k--&gt;m) rtrue;</span>
<span class="plain">rfalse;</span>
<span class="plain">];</span>
<span class="plain">[ WordInProperty wd obj prop k l m;</span>
<span class="plain">k = obj.&amp;prop; l = (obj.#prop)/WORDSIZE-1;</span>
<span class="plain">for (m=0 : m&lt;=l : m++)</span>
<span class="plain">if (wd == k--&gt;m) rtrue;</span>
<span class="plain">rfalse;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP54"></a><b>&#167;54. NounWord. </b><code class="display"><span class="extract">NounWord</span></code> (which takes no arguments) returns:
</p>
<ul class="items"><li>(a) 0 if the next word is not in the dictionary or is but does not carry the
"noun" bit in its dictionary entry,
</li><li>(b) 1 if it is a word meaning "me",
</li><li>(c) the index in the pronoun table (plus 2) of the value field of a pronoun,
if it is a pronoun,
</li><li>(d) the address in the dictionary if it is a recognised noun.
</li></ul>
<pre class="display">
<span class="plain">[ NounWord i j s;</span>
<span class="plain">i = NextWord();</span>
<span class="plain">if (i == 0) rfalse;</span>
<span class="plain">if (i == ME1__WD or ME2__WD or ME3__WD) return 1;</span>
<span class="plain">s = LanguagePronouns--&gt;0;</span>
<span class="plain">for (j=1 : j&lt;=s : j=j+3)</span>
<span class="plain">if (i == LanguagePronouns--&gt;j)</span>
<span class="plain">return j+2;</span>
<span class="plain">if ((i-&gt;#dict_par1)&amp;128 == 0) rfalse;</span>
<span class="plain">return i;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP55"></a><b>&#167;55. TryNumber. </b><code class="display"><span class="extract">TryNumber</span></code> takes word number <code class="display"><span class="extract">wordnum</span></code> and tries to parse it as an (unsigned)
decimal number or the name of a small number, returning
</p>
<p class="inwebparagraph"></p>
<ul class="items"><li>(a) -1000 if it is not a number
</li><li>(b) the number, if it has between 1 and 4 digits
</li><li>(c) 10000 if it has 5 or more digits.
</li></ul>
<p class="inwebparagraph">(The danger of allowing 5 digits is that Z-machine integers are only 16 bits
long, and anyway this routine isn't meant to be perfect: it only really needs
to be good enough to handle numeric descriptors such as those in TAKE 31 COINS
or DROP FOUR DAGGERS. In particular, it is not the way I7 "[number]" tokens
are parsed.)
</p>
<pre class="display">
<span class="plain">[ TryNumber wordnum i j c num len mul tot d digit;</span>
<span class="plain">i = wn; wn = wordnum; j = NextWord(); wn = i;</span>
<span class="plain">j = NumberWord(j); ! Test for verbal forms ONE to THIRTY</span>
<span class="plain">if (j &gt;= 1) return j;</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">i = wordnum*4+1; j = parse-&gt;i; num = j+buffer; len = parse-&gt;(i-1);</span>
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
<span class="plain">i = wordnum*3; j = parse--&gt;i; num = j+buffer; len = parse--&gt;(i-1);</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">if (len &gt;= 4) mul=1000;</span>
<span class="plain">if (len == 3) mul=100;</span>
<span class="plain">if (len == 2) mul=10;</span>
<span class="plain">if (len == 1) mul=1;</span>
<span class="plain">tot = 0; c = 0; len = len-1;</span>
<span class="plain">for (c=0 : c&lt;=len : c++) {</span>
<span class="plain">digit=num-&gt;c;</span>
<span class="plain">if (digit == '0') { d = 0; jump digok; }</span>
<span class="plain">if (digit == '1') { d = 1; jump digok; }</span>
<span class="plain">if (digit == '2') { d = 2; jump digok; }</span>
<span class="plain">if (digit == '3') { d = 3; jump digok; }</span>
<span class="plain">if (digit == '4') { d = 4; jump digok; }</span>
<span class="plain">if (digit == '5') { d = 5; jump digok; }</span>
<span class="plain">if (digit == '6') { d = 6; jump digok; }</span>
<span class="plain">if (digit == '7') { d = 7; jump digok; }</span>
<span class="plain">if (digit == '8') { d = 8; jump digok; }</span>
<span class="plain">if (digit == '9') { d = 9; jump digok; }</span>
<span class="plain">return -1000;</span>
<span class="plain">.digok;</span>
<span class="plain">tot = tot+mul*d; mul = mul/10;</span>
<span class="plain">}</span>
<span class="plain">if (len &gt; 3) tot=10000;</span>
<span class="plain">return tot;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP56"></a><b>&#167;56. Gender. </b><code class="display"><span class="extract">GetGender</span></code> returns 0 if the given animate object is female, and 1 if male,
and is abstracted as a routine in case something more elaborate is ever
needed.
</p>
<p class="inwebparagraph">For GNAs &mdash; gender/noun/animation combinations &mdash; see the Inform Designer's
Manual}, 4th edition.
</p>
<pre class="display">
<span class="plain">[ GetGender person;</span>
<span class="plain">if (person hasnt female) rtrue;</span>
<span class="plain">rfalse;</span>
<span class="plain">];</span>
<span class="plain">[ GetGNAOfObject obj case gender;</span>
<span class="plain">if (obj hasnt animate) case = 6;</span>
<span class="plain">if (obj has male) gender = male;</span>
<span class="plain">if (obj has female) gender = female;</span>
<span class="plain">if (obj has neuter) gender = neuter;</span>
<span class="plain">if (gender == 0) {</span>
<span class="plain">if (case == 0) gender = LanguageAnimateGender;</span>
<span class="plain">else gender = LanguageInanimateGender;</span>
<span class="plain">}</span>
<span class="plain">if (gender == female) case = case + 1;</span>
<span class="plain">if (gender == neuter) case = case + 2;</span>
<span class="plain">if (obj has pluralname) case = case + 3;</span>
<span class="plain">return case;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP57"></a><b>&#167;57. Noticing Plurals. </b></p>
<pre class="display">
<span class="plain">[ DetectPluralWord at n i w swn outcome;</span>
<span class="plain">swn = wn; wn = at;</span>
<span class="plain">for (i=0:i&lt;n:i++) {</span>
<span class="plain">w = NextWordStopped();</span>
<span class="plain">if (w == 0 or THEN1__WD or COMMA_WORD or -1) break;</span>
<span class="plain">if ((w-&gt;#dict_par1) &amp; $$00000100) {</span>
<span class="plain">parser_action = ##PluralFound;</span>
<span class="plain">outcome = true;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">wn = swn;</span>
<span class="plain">return outcome;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP58"></a><b>&#167;58. Pronoun Handling. </b></p>
<pre class="display">
<span class="plain">[ SetPronoun dword value x;</span>
<span class="plain">for (x=1 : x&lt;=LanguagePronouns--&gt;0 : x=x+3)</span>
<span class="plain">if (LanguagePronouns--&gt;x == dword) {</span>
<span class="plain">LanguagePronouns--&gt;(x+2) = value; return;</span>
<span class="plain">}</span>
<span class="plain">RunTimeError(14);</span>
<span class="plain">];</span>
<span class="plain">[ PronounValue dword x;</span>
<span class="plain">for (x=1 : x&lt;=LanguagePronouns--&gt;0 : x=x+3)</span>
<span class="plain">if (LanguagePronouns--&gt;x == dword)</span>
<span class="plain">return LanguagePronouns--&gt;(x+2);</span>
<span class="plain">return 0;</span>
<span class="plain">];</span>
<span class="plain">[ ResetVagueWords obj; PronounNotice(obj); ];</span>
<span class="plain">[ PronounNotice obj x bm g;</span>
<span class="plain">if (obj == player) return;</span>
<span class="plain">g = (GetGNAOfObject(obj));</span>
<span class="plain">bm = PowersOfTwo_TB--&gt;g;</span>
<span class="plain">for (x=1 : x&lt;=LanguagePronouns--&gt;0 : x=x+3)</span>
<span class="plain">if (bm &amp; (LanguagePronouns--&gt;(x+1)) ~= 0)</span>
<span class="plain">LanguagePronouns--&gt;(x+2) = obj;</span>
<span class="plain">if (((g % 6) &lt; 3) &amp;&amp; (obj has ambigpluralname)) {</span>
<span class="plain">g = g + 3;</span>
<span class="plain">bm = PowersOfTwo_TB--&gt;g;</span>
<span class="plain">for (x=1 : x&lt;=LanguagePronouns--&gt;0 : x=x+3)</span>
<span class="plain">if (bm &amp; (LanguagePronouns--&gt;(x+1)) ~= 0)</span>
<span class="plain">LanguagePronouns--&gt;(x+2) = obj;</span>
<span class="plain">}</span>
<span class="plain">];</span>
<span class="plain">[ PronounNoticeHeldObjects x;</span>
<span class="plain">#IFNDEF MANUAL_PRONOUNS;</span>
<span class="plain">objectloop(x in player) PronounNotice(x);</span>
<span class="plain">#ENDIF;</span>
<span class="plain">x = 0; ! To prevent a "not used" error</span>
<span class="plain">rfalse;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP59"></a><b>&#167;59. Yes/No Questions. </b></p>
<pre class="display">
<span class="plain">[ YesOrNo i j;</span>
<span class="plain">for (::) {</span>
<span class="plain">#Ifdef TARGET_ZCODE;</span>
<span class="plain">if (location == nothing || parent(player) == nothing) read buffer2 parse2;</span>
<span class="plain">else read buffer2 parse2 DrawStatusLine;</span>
<span class="plain">j = parse2-&gt;1;</span>
<span class="plain">#Ifnot; ! TARGET_GLULX;</span>
<span class="plain">if (location ~= nothing &amp;&amp; parent(player) ~= nothing) DrawStatusLine();</span>
<span class="plain">KeyboardPrimitive(buffer2, parse2);</span>
<span class="plain">j = parse2--&gt;0;</span>
<span class="plain">#Endif; ! TARGET_</span>
<span class="plain">if (j) { ! at least one word entered</span>
<span class="plain">i = parse2--&gt;1;</span>
<span class="plain">if (i == YES1__WD or YES2__WD or YES3__WD) rtrue;</span>
<span class="plain">if (i == NO1__WD or NO2__WD or NO3__WD) rfalse;</span>
<span class="plain">}</span>
<span class="plain">YES_OR_NO_QUESTION_INTERNAL_RM('A'); print "&gt; ";</span>
<span class="plain">}</span>
<span class="plain">];</span>
<span class="plain">[ YES_OR_NO_QUESTION_INTERNAL_R; ];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP60"></a><b>&#167;60. Number Words. </b>Not much of a parsing routine: we look through an array of pairs of number
words (single words) and their numeric equivalents.
</p>
<pre class="display">
<span class="plain">[ NumberWord o i n;</span>
<span class="plain">n = LanguageNumbers--&gt;0;</span>
<span class="plain">for (i=1 : i&lt;=n : i=i+2)</span>
<span class="plain">if (o == LanguageNumbers--&gt;i) return LanguageNumbers--&gt;(i+1);</span>
<span class="plain">return 0;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP61"></a><b>&#167;61. Choose Objects. </b>This material, the final body of code in the parser, is an I7 addition.
The I6 parser leaves it to the user to provide a <code class="display"><span class="extract">ChooseObjects</span></code> routine
to decide between possibilities when the situation is ambiguous. For I7
use, we provide a <code class="display"><span class="extract">ChooseObjects</span></code> which essentially runs the "does the
player mean" rulebook to decide, though this is not obvious from the
code below because it is hidden in the <code class="display"><span class="extract">CheckDPMR</span></code> routine &mdash; which
is defined in the Standard Rules, not here.
</p>
<pre class="display">
<span class="plain">!Constant COBJ_DEBUG;</span>
<span class="plain">! the highest value returned by CheckDPMR (see the Standard Rules)</span>
<span class="plain">Constant HIGHEST_DPMR_SCORE = 4;</span>
<span class="plain">Array alt_match_list --&gt; (MATCH_LIST_WORDS+1);</span>
<span class="plain">#ifdef TARGET_GLULX;</span>
<span class="plain">[ COBJ__Copy words from to i;</span>
<span class="plain">for (i=0: i&lt;words: i++)</span>
<span class="plain">to--&gt;i = from--&gt;i;</span>
<span class="plain">];</span>
<span class="plain">#ifnot;</span>
<span class="plain">[ COBJ__Copy words from to bytes;</span>
<span class="plain">bytes = words * 2;</span>
<span class="plain">@copy_table from to bytes;</span>
<span class="plain">];</span>
<span class="plain">#endif;</span>
<span class="plain">! swap alt_match_list with match_list/number_matched</span>
<span class="plain">[ COBJ__SwapMatches i x;</span>
<span class="plain">! swap the counts</span>
<span class="plain">x = number_matched;</span>
<span class="plain">number_matched = alt_match_list--&gt;0;</span>
<span class="plain">alt_match_list--&gt;0 = x;</span>
<span class="plain">! swap the values</span>
<span class="plain">if (x &lt; number_matched) x = number_matched;</span>
<span class="plain">for (i=x: i&gt;0: i--) {</span>
<span class="plain">x = match_list--&gt;(i-1);</span>
<span class="plain">match_list--&gt;(i-1) = alt_match_list--&gt;i;</span>
<span class="plain">alt_match_list--&gt;i = x;</span>
<span class="plain">}</span>
<span class="plain">];</span>
<span class="plain">[ ChooseObjects obj code l i swn spcount;</span>
<span class="plain">if (code&lt;2) rfalse;</span>
<span class="plain">if (cobj_flag == 1) {</span>
<span class="plain">.CodeOne;</span>
<span class="plain">if (parameters &gt; 0) {</span>
<span class="plain">#ifdef COBJ_DEBUG;</span>
<span class="plain">print "[scoring ", (the) obj, " (second)]^";</span>
<span class="plain">#endif;</span>
<span class="plain">return ScoreDabCombo(parser_results--&gt;INP1_PRES, obj);</span>
<span class="plain">} else {</span>
<span class="plain">#ifdef COBJ_DEBUG;</span>
<span class="plain">print "[scoring ", (the) obj, " (first) in ",</span>
<span class="plain">alt_match_list--&gt;0, " combinations]^";</span>
<span class="plain">#endif;</span>
<span class="plain">l = 0;</span>
<span class="plain">for (i=1: i&lt;=alt_match_list--&gt;0: i++) {</span>
<span class="plain">spcount = ScoreDabCombo(obj, alt_match_list--&gt;i);</span>
<span class="plain">if (spcount == HIGHEST_DPMR_SCORE) {</span>
<span class="plain">#ifdef COBJ_DEBUG;</span>
<span class="plain">print "[scored ", spcount, " - best possible]^";</span>
<span class="plain">#endif;</span>
<span class="plain">return spcount;</span>
<span class="plain">}</span>
<span class="plain">if (spcount&gt;l) l = spcount;</span>
<span class="plain">}</span>
<span class="plain">return l;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (cobj_flag == 2) {</span>
<span class="plain">.CodeTwo;</span>
<span class="plain">#ifdef COBJ_DEBUG;</span>
<span class="plain">print "[scoring ", (the) obj, " (simple); parameters = ", parameters,</span>
<span class="plain">" aw = ", advance_warning, "]^";</span>
<span class="plain">#endif;</span>
<span class="plain">@push action_to_be;</span>
<span class="plain">if (parameters==0) {</span>
<span class="plain">if (advance_warning &gt; 0)</span>
<span class="plain">l = ScoreDabCombo(obj, advance_warning);</span>
<span class="plain">else</span>
<span class="plain">l = ScoreDabCombo(obj, 0);</span>
<span class="plain">} else {</span>
<span class="plain">l = ScoreDabCombo(parser_results--&gt;INP1_PRES, obj);</span>
<span class="plain">}</span>
<span class="plain">@pull action_to_be;</span>
<span class="plain">return l;</span>
<span class="plain">}</span>
<span class="plain">#ifdef COBJ_DEBUG;</span>
<span class="plain">print "[choosing a cobj strategy: ";</span>
<span class="plain">#endif;</span>
<span class="plain">swn = wn;</span>
<span class="plain">spcount = pcount;</span>
<span class="plain">while (line_ttype--&gt;pcount == PREPOSITION_TT) pcount++;</span>
<span class="plain">if (line_ttype--&gt;pcount == ELEMENTARY_TT) {</span>
<span class="plain">if (line_tdata--&gt;pcount == TOPIC_TOKEN) {</span>
<span class="plain">pcount = spcount;</span>
<span class="plain">jump CodeTwo;</span>
<span class="plain">}</span>
<span class="plain">while (wn &lt;= num_words) {</span>
<span class="plain">l = NextWordStopped(); wn--;</span>
<span class="plain">if (l == THEN1__WD) break;</span>
<span class="plain">if ( (l ~= -1 or 0) &amp;&amp; (l-&gt;#dict_par1) &amp;8 ) { wn++; continue; } ! if preposition</span>
<span class="plain">if (l == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) { wn++; continue; }</span>
<span class="plain">SafeSkipDescriptors();</span>
<span class="plain">! save the current match state</span>
<span class="plain">@push match_length; @push token_filter; @push match_from;</span>
<span class="plain">alt_match_list--&gt;0 = number_matched;</span>
<span class="plain">COBJ__Copy(number_matched, match_list, alt_match_list+WORDSIZE);</span>
<span class="plain">! now get all the matches for the second noun</span>
<span class="plain">match_length = 0; number_matched = 0; match_from = wn;</span>
<span class="plain">token_filter = 0;</span>
<span class="plain">SearchScope(actor, actors_location, line_tdata--&gt;pcount);</span>
<span class="plain">#ifdef COBJ_DEBUG;</span>
<span class="plain">print number_matched, " possible second nouns]^";</span>
<span class="plain">#endif;</span>
<span class="plain">wn = swn;</span>
<span class="plain">cobj_flag = 1;</span>
<span class="plain">! restore match variables</span>
<span class="plain">COBJ__SwapMatches();</span>
<span class="plain">@pull match_from; @pull token_filter; @pull match_length;</span>
<span class="plain">pcount = spcount;</span>
<span class="plain">jump CodeOne;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">pcount = spcount;</span>
<span class="plain">wn = swn;</span>
<span class="plain">#ifdef COBJ_DEBUG;</span>
<span class="plain">print "nothing interesting]^";</span>
<span class="plain">#endif;</span>
<span class="plain">cobj_flag = 2;</span>
<span class="plain">jump CodeTwo;</span>
<span class="plain">];</span>
<span class="plain">[ ScoreDabCombo a b result;</span>
<span class="plain">@push action; @push act_requester; @push noun; @push second;</span>
<span class="plain">action = action_to_be;</span>
<span class="plain">act_requester = player;</span>
<span class="plain">if (action_reversed) { noun = b; second = a; }</span>
<span class="plain">else { noun = a; second = b; }</span>
<span class="plain">result = CheckDPMR();</span>
<span class="plain">@pull second; @pull noun; @pull act_requester; @pull action;</span>
<span class="plain">#ifdef COBJ_DEBUG;</span>
<span class="plain">print "[", (the) a, " / ", (the) b, " =&gt; ", result, "]^";</span>
<span class="plain">#endif;</span>
<span class="plain">return result;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP62"></a><b>&#167;62. Default Topic. </b>A default value for the I7 sort-of-kind "topic", which never matches.
</p>
<pre class="display">
<span class="plain">[ DefaultTopic; return GPR_FAIL; ];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP63"></a><b>&#167;63. Recognition-only-GPR. </b>An I6 general parsing routine to look at words from the position marker
<code class="display"><span class="extract">wn</span></code> in the player's command to see if they match the contents of the
text <code class="display"><span class="extract">txt</span></code>, returning either <code class="display"><span class="extract">GPR_PREPOSITION</span></code> or <code class="display"><span class="extract">GPR_FAIL</span></code>
according to whether a match could be made. This is used when the an
object's name is set to include one of its properties, and the property in
question is a text: "A flowerpot is a kind of thing. A flowerpot
has a text called pattern. Understand the pattern property as
describing a flowerpot." When the player types EXAMINE STRIPED FLOWERPOT,
and there is a flowerpot in scope, the following routine is called to
test whether its pattern property &mdash; a text &mdash; matches any words
at the position STRIPED FLOWERPOT. Assuming a pot does indeed have the
pattern "striped", the routine advances <code class="display"><span class="extract">wn</span></code> by 1 and returns
<code class="display"><span class="extract">GPR_PREPOSITION</span></code> to indicate a match.
</p>
<p class="inwebparagraph">This kind of GPR is called a "recognition-only-GPR", because it only
recognises an existing value: it doesn't parse a new one.
</p>
<pre class="display">
<span class="plain">[ TEXT_TY_ROGPR txt p cp r;</span>
<span class="plain">if (txt == 0) return GPR_FAIL;</span>
<span class="plain">cp = txt--&gt;0; p = TEXT_TY_Temporarily_Transmute(txt);</span>
<span class="plain">r = TEXT_TY_ROGPRI(txt);</span>
<span class="plain">TEXT_TY_Untransmute(txt, p, cp);</span>
<span class="plain">return r;</span>
<span class="plain">];</span>
<span class="plain">[ TEXT_TY_ROGPRI txt</span>
<span class="plain">pos len wa wl wpos bdm ch own;</span>
<span class="plain">bdm = true; own = wn;</span>
<span class="plain">len = BlkValueLBCapacity(txt);</span>
<span class="plain">for (pos=0: pos&lt;=len: pos++) {</span>
<span class="plain">if (pos == len) ch = 0; else ch = BlkValueRead(txt, pos);</span>
<span class="plain">if (ch == 32 or 9 or 10 or 0) {</span>
<span class="plain">if (bdm) continue;</span>
<span class="plain">bdm = true;</span>
<span class="plain">if (wpos ~= wl) return GPR_FAIL;</span>
<span class="plain">if (ch == 0) break;</span>
<span class="plain">} else {</span>
<span class="plain">if (bdm) {</span>
<span class="plain">bdm = false;</span>
<span class="plain">if (NextWordStopped() == -1) return GPR_FAIL;</span>
<span class="plain">wa = WordAddress(wn-1);</span>
<span class="plain">wl = WordLength(wn-1);</span>
<span class="plain">wpos = 0;</span>
<span class="plain">}</span>
<span class="plain">if (wa-&gt;wpos ~= ch or TEXT_TY_RevCase(ch)) return GPR_FAIL;</span>
<span class="plain">wpos++;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">if (wn == own) return GPR_FAIL; ! Progress must be made to avoid looping</span>
<span class="plain">return GPR_PREPOSITION;</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP64"></a><b>&#167;64. RunRoutines. </b>This function may not be very well-named, but the idea is to take a property
of a given object and either to print it (and return <code class="display"><span class="extract">true</span></code>) if it's a string,
and call it (and pass along its return value) if it's a routine. If the
object does not provide the property, we act on the default value for the
property if it has one, and otherwise do nothing (and return <code class="display"><span class="extract">false</span></code>).
</p>
<p class="inwebparagraph">The I6 pseudo-object <code class="display"><span class="extract">thedark</span></code> is used to give the impression that Darkness
is a room in its own right, which is not really true. Note that it is not
permitted to have properties other than the three named here: all other
properties are redirected to the current location's object.
</p>
<p class="inwebparagraph">Properties with numbers under <code class="display"><span class="extract">INDIV_PROP_START</span></code> are "common properties".
These come along with a table of default values, so that it is meaningful
(in I6, anyway) to read them even when they are not provided (so that the
address, returned by the <code class="display"><span class="extract">.&amp;</span></code> operator, is zero).
</p>
<pre class="display">
<span class="plain">[ RunRoutines obj prop;</span>
<span class="plain">if (obj == thedark) obj = real_location;</span>
<span class="plain">if ((obj.&amp;prop == 0) &amp;&amp; (prop &gt;= INDIV_PROP_START)) rfalse;</span>
<span class="plain">return obj.prop();</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP65"></a><b>&#167;65. Setting the Player's Command. </b>In effect, the text typed most recently by the player is a sort of
text already, though it isn't in text format, and doesn't live on
the heap.
</p>
<pre class="display">
<span class="plain">[ SetPlayersCommand from_txt i len at p cp;</span>
<span class="plain">cp = from_txt--&gt;0; p = TEXT_TY_Temporarily_Transmute(from_txt);</span>
<span class="plain">len = TEXT_TY_CharacterLength(from_txt);</span>
<span class="plain">if (len &gt; 118) len = 118;</span>
<span class="plain">#ifdef TARGET_ZCODE;</span>
<span class="plain">buffer-&gt;1 = len; at = 2;</span>
<span class="plain">#ifnot;</span>
<span class="plain">buffer--&gt;0 = len; at = 4;</span>
<span class="plain">#endif;</span>
<span class="plain">for (i=0:i&lt;len:i++) buffer-&gt;(i+at) = CharToCase(BlkValueRead(from_txt, i), 0);</span>
<span class="plain">for (:at+i&lt;120:i++) buffer-&gt;(at+i) = ' ';</span>
<span class="plain">VM_Tokenise(buffer, parse);</span>
<span class="plain">players_command = 100 + WordCount(); ! The snippet variable "player's command"</span>
<span class="plain">TEXT_TY_Untransmute(from_txt, p, cp);</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP66"></a><b>&#167;66. Multiple Object List. </b>The parser uses one data structure which is really a list: but which can't
be represented as such because the heap might not exist. This is the multiple
object list, which is used to handle commands like TAKE ALL by firing off a
sequence of actions with one of the objects taken from entries in turn of
the list. The following converts it to a list structure.
</p>
<pre class="display">
<span class="plain">[ LIST_OF_TY_Mol list len i;</span>
<span class="plain">if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0;</span>
<span class="plain">len = multiple_object--&gt;0;</span>
<span class="plain">LIST_OF_TY_SetLength(list, len);</span>
<span class="plain">for (i=1: i&lt;=len: i++)</span>
<span class="plain">LIST_OF_TY_PutItem(list, i, multiple_object--&gt;i);</span>
<span class="plain">return list;</span>
<span class="plain">];</span>
<span class="plain">[ LIST_OF_TY_Set_Mol list len i;</span>
<span class="plain">if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0;</span>
<span class="plain">len = BlkValueRead(list, LIST_LENGTH_F);</span>
<span class="plain">if (len &gt; 63) len = 63;</span>
<span class="plain">multiple_object--&gt;0 = len;</span>
<span class="plain">for (i=1: i&lt;=len: i++)</span>
<span class="plain">multiple_object--&gt;i = BlkValueRead(list, LIST_ITEM_BASE+i-1);</span>
<span class="plain">];</span>
</pre>
<p class="inwebparagraph"></p>
<hr class="tocbar">
<ul class="toc"><li><a href="S-ot3.html">Back to 'OutOfWorld Template'</a></li><li><a href="S-pt2.html">Continue with 'Printing Template'</a></li></ul><hr class="tocbar">
<!--End of weave-->
</body>
</html>