mirror of
https://github.com/ganelson/inform.git
synced 2024-07-08 18:14:21 +03:00
4469 lines
263 KiB
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">★</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">§1. Grammar Line Variables</a></li><li><a href="#SP2">§2. Grammar Token Variables</a></li><li><a href="#SP3">§3. Match List Variables</a></li><li><a href="#SP4">§4. Words</a></li><li><a href="#SP5">§5. Snippets</a></li><li><a href="#SP6">§6. Unpacking Grammar Lines</a></li><li><a href="#SP7">§7. Keyboard Primitive</a></li><li><a href="#SP8">§8. Reading the Command</a></li><li><a href="#SP9">§9. Parser Proper</a></li><li><a href="#SP10">§10. Parser Letter A</a></li><li><a href="#SP11">§11. Parser Letter B</a></li><li><a href="#SP12">§12. Parser Letter C</a></li><li><a href="#SP13">§13. Parser Letter D</a></li><li><a href="#SP14">§14. Parser Letter E</a></li><li><a href="#SP15">§15. Parser Letter F</a></li><li><a href="#SP16">§16. Parser Letter G</a></li><li><a href="#SP17">§17. Parser Letter H</a></li><li><a href="#SP18">§18. Parser Letter I</a></li><li><a href="#SP19">§19. Parser Letter J</a></li><li><a href="#SP20">§20. Parser Letter K</a></li><li><a href="#SP21">§21. End of Parser Proper</a></li><li><a href="#SP22">§22. Internal Rule</a></li><li><a href="#SP23">§23. Parse Token</a></li><li><a href="#SP24">§24. Parse Token Letter A</a></li><li><a href="#SP25">§25. Parse Token Letter B</a></li><li><a href="#SP26">§26. Parse Token Letter C</a></li><li><a href="#SP27">§27. Parse Token Letter D</a></li><li><a href="#SP28">§28. Parse Token Letter E</a></li><li><a href="#SP29">§29. Parse Token Letter F</a></li><li><a href="#SP30">§30. Descriptors</a></li><li><a href="#SP31">§31. Parsing Descriptors</a></li><li><a href="#SP32">§32. Preposition Chain</a></li><li><a href="#SP33">§33. Creature</a></li><li><a href="#SP34">§34. Noun Domain</a></li><li><a href="#SP35">§35. Adjudicate</a></li><li><a href="#SP36">§36. ReviseMulti</a></li><li><a href="#SP37">§37. Match List</a></li><li><a href="#SP38">§38. ScoreMatchL</a></li><li><a href="#SP39">§39. BestGuess</a></li><li><a href="#SP40">§40. SingleBestGuess</a></li><li><a href="#SP41">§41. Identical</a></li><li><a href="#SP42">§42. Print Command</a></li><li><a href="#SP43">§43. CantSee</a></li><li><a href="#SP44">§44. Multiple Object List</a></li><li><a href="#SP45">§45. Scope</a></li><li><a href="#SP46">§46. Scope Level 0</a></li><li><a href="#SP47">§47. SearchScope</a></li><li><a href="#SP48">§48. ScopeWithin</a></li><li><a href="#SP49">§49. DoScopeActionAndRecurse</a></li><li><a href="#SP50">§50. DoScopeAction</a></li><li><a href="#SP51">§51. Parsing Object Names</a></li><li><a href="#SP52">§52. TryGivenObject</a></li><li><a href="#SP53">§53. Refers</a></li><li><a href="#SP54">§54. NounWord</a></li><li><a href="#SP55">§55. TryNumber</a></li><li><a href="#SP56">§56. Gender</a></li><li><a href="#SP57">§57. Noticing Plurals</a></li><li><a href="#SP58">§58. Pronoun Handling</a></li><li><a href="#SP59">§59. Yes/No Questions</a></li><li><a href="#SP60">§60. Number Words</a></li><li><a href="#SP61">§61. Choose Objects</a></li><li><a href="#SP62">§62. Default Topic</a></li><li><a href="#SP63">§63. Recognition-only-GPR</a></li><li><a href="#SP64">§64. RunRoutines</a></li><li><a href="#SP65">§65. Setting the Player's Command</a></li><li><a href="#SP66">§66. Multiple Object List</a></li></ul><hr class="tocbar">
|
||
|
|
||
|
<p class="inwebparagraph"><a id="SP1"></a><b>§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 --> 32; ! For the current pattern match</span>
|
||
|
<span class="plain">Global pcount; ! and a marker within it</span>
|
||
|
<span class="plain">Array pattern2 --> 32; ! And another, which stores the best match</span>
|
||
|
<span class="plain">Global pcount2; ! so far</span>
|
||
|
|
||
|
<span class="plain">Array line_ttype-->32; ! For storing an analysed grammar line</span>
|
||
|
<span class="plain">Array line_tdata-->32;</span>
|
||
|
<span class="plain">Array line_token-->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 -> 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>§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>§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 --> MATCH_LIST_WORDS; ! An array of matched objects so far</span>
|
||
|
<span class="plain">Array match_classes --> MATCH_LIST_WORDS; ! An array of equivalence classes for them</span>
|
||
|
<span class="plain">Array match_scores --> 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>§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">-></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->1; ];</span>
|
||
|
<span class="plain">[ WordAddress wordnum; return buffer + parse->(wordnum*4+1); ];</span>
|
||
|
<span class="plain">[ WordLength wordnum; return parse->(wordnum*4); ];</span>
|
||
|
<span class="plain">#Ifnot;</span>
|
||
|
<span class="plain">[ WordCount; return parse-->0; ];</span>
|
||
|
<span class="plain">[ WordAddress wordnum; return buffer + parse-->(wordnum*3); ];</span>
|
||
|
<span class="plain">[ WordLength wordnum; return parse-->(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->1; i = w*2-1;</span>
|
||
|
<span class="plain">#Ifnot; wc = p-->0; i = w*3-2; #Endif;</span>
|
||
|
<span class="plain">if ((w < 1) || (w > wc)) return 0;</span>
|
||
|
<span class="plain">j = p-->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->1; i = wn*2-1;</span>
|
||
|
<span class="plain">#Ifnot; wc = parse-->0; i = wn*3-2; #Endif;</span>
|
||
|
<span class="plain">wn++;</span>
|
||
|
<span class="plain">if ((wn < 2) || (wn > wc+1)) return 0;</span>
|
||
|
<span class="plain">j = parse-->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->1; #Ifnot; wc = parse-->0; #Endif;</span>
|
||
|
<span class="plain">if ((wn < 1) || (wn > 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>§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<= w_1<= w_2<= 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<w1) || (w1<1) || (w2>WordCount())) {</span>
|
||
|
<span class="plain">if ((w1 == 1) && (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<=to: i++) print (char) i->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<w1) || (w1<1)) {</span>
|
||
|
<span class="plain">if ((w1 == 1) && (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 <= WordCount()) endsnippet = 100*nextw + (WordCount() - nextw + 1);</span>
|
||
|
<span class="plain">buffer2-->0 = 120;</span>
|
||
|
<span class="plain">newlen = VM_PrintToBuffer(buffer2, 120, SpliceSnippet__TextPrinter, t, endsnippet);</span>
|
||
|
<span class="plain">for (i=0: (i<newlen) && (at+i<120): i++) buffer->(at+i) = buffer2->(WORDSIZE+i);</span>
|
||
|
<span class="plain">#Ifdef TARGET_ZCODE; buffer->1 = at+i; #ifnot; buffer-->0 = at+i; #endif;</span>
|
||
|
<span class="plain">for (:at+i<120:i++) buffer->(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<w1) || (w1<1)) {</span>
|
||
|
<span class="plain">if ((w1 == 1) && (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>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>§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<32 : i++) {</span>
|
||
|
<span class="plain">line_token-->i = ENDIT_TOKEN;</span>
|
||
|
<span class="plain">line_ttype-->i = ELEMENTARY_TT;</span>
|
||
|
<span class="plain">line_tdata-->i = ENDIT_TOKEN;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">#Ifdef TARGET_ZCODE;</span>
|
||
|
<span class="plain">action_to_be = 256*(line_address->0) + line_address->1;</span>
|
||
|
<span class="plain">action_reversed = ((action_to_be & $400) ~= 0);</span>
|
||
|
<span class="plain">action_to_be = action_to_be & $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->2) & 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->0 == ENDIT_TOKEN) break;</span>
|
||
|
<span class="plain">line_token-->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-->i = found_ttype;</span>
|
||
|
<span class="plain">line_tdata-->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->0) & $$1111;</span>
|
||
|
<span class="plain">found_tdata = (token+1)-->0;</span>
|
||
|
<span class="plain">];</span>
|
||
|
</pre>
|
||
|
|
||
|
<p class="inwebparagraph"></p>
|
||
|
|
||
|
<p class="inwebparagraph"><a id="SP7"></a><b>§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>§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 —
|
||
|
it is essentially an internal saved game, in the VM interpreter's memory
|
||
|
rather than in an external file — 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<64 : i++) oops_workspace->i = a_buffer->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->0 = INPUT_BUFFER_LEN;</span>
|
||
|
<span class="plain">a_table->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->1; #Ifnot; nw = a_table-->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-->1 is the first word on both the Z-machine and Glulx</span>
|
||
|
|
||
|
<span class="plain">w = a_table-->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 > 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<INPUT_BUFFER_LEN : i++) buffer2->i = a_buffer->i;</span>
|
||
|
<span class="plain">#Ifdef TARGET_ZCODE;</span>
|
||
|
<span class="plain">x1 = a_table->9; ! Start of word following "oops"</span>
|
||
|
<span class="plain">x2 = a_table->8; ! Length of word following "oops"</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">x1 = a_table-->6; ! Start of word following "oops"</span>
|
||
|
<span class="plain">x2 = a_table-->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<64 : i++) a_buffer->i = oops_workspace->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->(4*oops_from + 1); ! Start of word to go</span>
|
||
|
<span class="plain">w2 = a_table->(4*oops_from); ! Length of word to go</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">w = a_table-->(3*oops_from); ! Start of word to go</span>
|
||
|
<span class="plain">w2 = a_table-->(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<w2 : i++) a_buffer->(i+w) = ' ';</span>
|
||
|
|
||
|
<span class="plain">if (w2 < 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>=w+x2 : i--)</span>
|
||
|
<span class="plain">a_buffer->i = a_buffer->(i-x2+w2);</span>
|
||
|
|
||
|
<span class="plain">! ...increasing buffer size accordingly.</span>
|
||
|
<span class="plain">#Ifdef TARGET_ZCODE;</span>
|
||
|
<span class="plain">a_buffer->1 = (a_buffer->1) + (x2-w2);</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">a_buffer-->0 = (a_buffer-->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<x2 : i++) a_buffer->(i+w) = buffer2->(i+x1);</span>
|
||
|
|
||
|
<span class="plain">VM_Tokenise(a_buffer, a_table);</span>
|
||
|
<span class="plain">#Ifdef TARGET_ZCODE; nw = a_table->1; #Ifnot; nw = a_table-->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) && (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 & 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>§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-->ACTION_PRES = 0;</span>
|
||
|
<span class="plain">parser_results-->NO_INPS_PRES = 0;</span>
|
||
|
<span class="plain">parser_results-->INP1_PRES = 0;</span>
|
||
|
<span class="plain">parser_results-->INP2_PRES = 0;</span>
|
||
|
<span class="plain">meta = false;</span>
|
||
|
</pre>
|
||
|
|
||
|
<p class="inwebparagraph"></p>
|
||
|
|
||
|
<p class="inwebparagraph"><a id="SP10"></a><b>§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 > 0) i = WordAddress(verb_wordnum); else i = WordAddress(1);</span>
|
||
|
<span class="plain">j = WordAddress(wn);</span>
|
||
|
<span class="plain">if (i<=j) for (: i<j : i++) i->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 > num_words) j = INPUT_BUFFER_LEN-1;</span>
|
||
|
<span class="plain">else j = WordAddress(wn)-buffer;</span>
|
||
|
<span class="plain">for (: i<j : i++) buffer3->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 >= 2) {</span>
|
||
|
<span class="plain">print "[ ";</span>
|
||
|
<span class="plain">for (i=0 : i<num_words : i++) {</span>
|
||
|
|
||
|
<span class="plain">#Ifdef TARGET_ZCODE;</span>
|
||
|
<span class="plain">j = parse-->(i*2 + 1);</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">j = parse-->(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<l : m++) print (char) k->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-->0) >= 0 &&</span>
|
||
|
<span class="plain">UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 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->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->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-->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<INPUT_BUFFER_LEN : i++) buffer->i = buffer3->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<INPUT_BUFFER_LEN : i++) buffer3->i = buffer->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 >= 2 && 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) && (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-->ACTION_PRES = action;</span>
|
||
|
<span class="plain">parser_results-->NO_INPS_PRES = 0;</span>
|
||
|
<span class="plain">parser_results-->INP1_PRES = noun;</span>
|
||
|
<span class="plain">parser_results-->INP2_PRES = second;</span>
|
||
|
<span class="plain">if (noun) parser_results-->NO_INPS_PRES = 1;</span>
|
||
|
<span class="plain">if (second) parser_results-->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>§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->#dict_par1) & 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) && (l ofclass K3_direction)) {</span>
|
||
|
<span class="plain">parser_results-->ACTION_PRES = ##Go;</span>
|
||
|
<span class="plain">parser_results-->NO_INPS_PRES = 1;</span>
|
||
|
<span class="plain">parser_results-->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>§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<=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 && ((verb_word->#dict_par1) & 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 && 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 && ((verb_word->#dict_par1) & 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 >= 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>§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->#dict_par1) & 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->#dict_par1) & 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 && 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-->0)-->i;</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">syntax = (#grammar_table)-->(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->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 >= 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>§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<=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 >= 1) {</span>
|
||
|
<span class="plain">if (parser_trace >= 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-->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>§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 <one or more prepositions> 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-->pcount ~= ENDIT_TOKEN : pcount++) {</span>
|
||
|
<span class="plain">scope_token = 0;</span>
|
||
|
|
||
|
<span class="plain">if (line_ttype-->pcount ~= PREPOSITION_TT) i++;</span>
|
||
|
|
||
|
<span class="plain">if (line_ttype-->pcount == ELEMENTARY_TT) {</span>
|
||
|
<span class="plain">if (line_tdata-->pcount == MULTI_TOKEN) m = true;</span>
|
||
|
<span class="plain">if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN && 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 >= 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-->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 > num_words) ||</span>
|
||
|
<span class="plain">(l && (l->#dict_par1) & 8 ~= 0));</span>
|
||
|
|
||
|
<span class="plain">if (wn > num_words) {</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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-->pcount)->0 & $20 ~= 0) {</span>
|
||
|
<span class="plain">pcount++;</span>
|
||
|
<span class="plain">while ((line_token-->pcount ~= ENDIT_TOKEN) &&</span>
|
||
|
<span class="plain">((line_token-->pcount)->0 & $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 >= num_words) ||</span>
|
||
|
<span class="plain">(l && (l->#dict_par1) & 8 ~= 0));</span>
|
||
|
|
||
|
<span class="plain">if (l && (l->#dict_par1) & 8) continue;</span>
|
||
|
|
||
|
<span class="plain">! lookahead failed</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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 <= num_words) l = NextWord();</span>
|
||
|
<span class="plain">} until (line_ttype-->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-->pcount == ELEMENTARY_TT) &&</span>
|
||
|
<span class="plain">(line_tdata-->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-->INP1_PRES; @push k; @push parameters;</span>
|
||
|
<span class="plain">parameters = 1; parser_results-->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-->INP1_PRES = k;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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 >= 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 >= 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 && params_wanted == 1 && 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-->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>§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-->(pcount-1) == ENDIT_TOKEN) {</span>
|
||
|
<span class="plain">if (pcount >= 2) {</span>
|
||
|
<span class="plain">while ((((line_token-->(pcount-2))->0) & $10) ~= 0) pcount--;</span>
|
||
|
<span class="plain">AnalyseToken(line_token-->(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 >= 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-->pcount = PATTERN_NULL; scope_token = 0;</span>
|
||
|
|
||
|
<span class="plain">token = line_token-->(pcount-1);</span>
|
||
|
<span class="plain">lookahead = line_token-->pcount;</span>
|
||
|
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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 >= GPR_NOUN) && (l < -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 && (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 < 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-->(parameters+INP1_PRES) = l;</span>
|
||
|
<span class="plain">parameters++;</span>
|
||
|
<span class="plain">pattern-->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 >= 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 <= 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<32 : m++) pattern2-->m = pattern-->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 >= 1) {</span>
|
||
|
<span class="plain">if (parser_results-->INP1_PRES == 0) {</span>
|
||
|
<span class="plain">l = ReviseMulti(parser_results-->INP2_PRES);</span>
|
||
|
<span class="plain">if (l ~= 0) { etype = l; parser_results-->ACTION_PRES = action_to_be; break; }</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">if (parameters >= 2) {</span>
|
||
|
<span class="plain">if (parser_results-->INP2_PRES == 0) {</span>
|
||
|
<span class="plain">l = ReviseMulti(parser_results-->INP1_PRES);</span>
|
||
|
<span class="plain">if (l ~= 0) { etype = l; break; }</span>
|
||
|
<span class="plain">} else {</span>
|
||
|
<span class="plain">k = parser_results-->INP1_PRES; l = parser_results-->INP2_PRES;</span>
|
||
|
<span class="plain">if (k && l) {</span>
|
||
|
<span class="plain">if ((multi_context==MULTIEXCEPT_TOKEN && k == l) ||</span>
|
||
|
<span class="plain">((multi_context==MULTIINSIDE_TOKEN && k notin l && l notin k))) {</span>
|
||
|
<span class="plain">best_etype = NOTHING_PE;</span>
|
||
|
<span class="plain">parser_results-->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 && parser_results-->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 >= 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-->ACTION_PRES = action_to_be;</span>
|
||
|
<span class="plain">parser_results-->NO_INPS_PRES = parameters;</span>
|
||
|
|
||
|
<span class="plain">! ...reverse first and second parameters if need be...</span>
|
||
|
|
||
|
<span class="plain">if (action_reversed && parameters == 2) {</span>
|
||
|
<span class="plain">i = parser_results-->INP1_PRES;</span>
|
||
|
<span class="plain">parser_results-->INP1_PRES = parser_results-->INP2_PRES;</span>
|
||
|
<span class="plain">parser_results-->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 > 0 && parser_results-->INP1_PRES >= 2)</span>
|
||
|
<span class="plain">PronounNotice(parser_results-->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 > best_etype) best_etype = etype;</span>
|
||
|
<span class="plain">if (etype ~= ASKSCOPE_PE && etype > 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 && 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>§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-->ACTION_PRES = ##Answer;</span>
|
||
|
<span class="plain">parser_results-->NO_INPS_PRES = 2;</span>
|
||
|
<span class="plain">parser_results-->INP1_PRES = actor;</span>
|
||
|
<span class="plain">parser_results-->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>§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 && 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<32 : m++) pattern-->m = pattern2-->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-->ACTION_PRES == ##Remove &&</span>
|
||
|
<span class="plain">parser_results-->INP2_PRES ofclass Object) {</span>
|
||
|
<span class="plain">noun = parser_results-->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 && 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-->ACTION_PRES = 0;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">if (parser_results-->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>§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>§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 > 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 > 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>§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>§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>§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>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 > 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<number_matched: i++) {</span>
|
||
|
<span class="plain">t = match_list-->i; @push t;</span>
|
||
|
<span class="plain">t = match_classes-->i; @push t;</span>
|
||
|
<span class="plain">t = match_scores-->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 > 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>=0: i--) {</span>
|
||
|
<span class="plain">@pull t; match_scores-->i = t;</span>
|
||
|
<span class="plain">@pull t; match_classes-->i = t;</span>
|
||
|
<span class="plain">@pull t; match_list-->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>§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 >= 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 >= 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>=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-->(token_n+1) ~= PREPOSITION_TT) &&</span>
|
||
|
<span class="plain">(line_token-->(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 && (line_ttype-->(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->0) & $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 > num_words) {</span>
|
||
|
<span class="plain">if (inferfrom==0 && parameters<params_wanted) {</span>
|
||
|
<span class="plain">inferfrom = pcount; inferword = token;</span>
|
||
|
<span class="plain">pattern-->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-->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-->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 >= 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 >= 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 >= 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>§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>§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 >= 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<=LanguageDescriptors-->0 : l=l+4)</span>
|
||
|
<span class="plain">if (o == LanguageDescriptors-->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 >= 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>§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-->0;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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&A</span>
|
||
|
<span class="plain">if (indef_wanted == INDEF_ALL_WANTED && l == 0 && number_matched == 0)</span>
|
||
|
<span class="plain">l = 1; ! ReviseMulti if TAKE ALL FROM empty container</span>
|
||
|
|
||
|
<span class="plain">if (token_allows_multiple && ~~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 && 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 >= 3) {</span>
|
||
|
<span class="plain">if (l > 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-->0;</span>
|
||
|
<span class="plain">for (j=i+1 : j<=k : j++)</span>
|
||
|
<span class="plain">print " Entry ", j, ": ", (The) multiple_object-->j,</span>
|
||
|
<span class="plain">" (", multiple_object-->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-->0; ! (with either parity)</span>
|
||
|
<span class="plain">multiple_object-->0 = i;</span>
|
||
|
<span class="plain">for (j=i+1 : j<=k : j++) {</span>
|
||
|
<span class="plain">if (and_parity) MultiAdd(multiple_object-->j);</span>
|
||
|
<span class="plain">else MultiSub(multiple_object-->j);</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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 && 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) && (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 >= 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">! > 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 >= 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>§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 >= 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-->0 = 1;</span>
|
||
|
<span class="plain">multiple_object-->1 = single_object;</span>
|
||
|
<span class="plain">many_flag = true;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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>§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 && indef_type & PLURAL_BIT ~= 0) {</span>
|
||
|
<span class="plain">if (token == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) multi_context = token;</span>
|
||
|
<span class="plain">if (indef_wanted < INDEF_ALL_WANTED && indef_wanted > 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 && indef_guess_p == 1) {</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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 > 0 || prev_indef_wanted > 0) && (~~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>§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 > 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<=LanguageDescriptors-->0 : x=x+4)</span>
|
||
|
<span class="plain">if (o == LanguageDescriptors-->x) {</span>
|
||
|
<span class="plain">type = LanguageDescriptors-->(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>§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 > 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<=LanguageDescriptors-->0 : x=x+4)</span>
|
||
|
<span class="plain">if (o == LanguageDescriptors-->x) {</span>
|
||
|
<span class="plain">flag = true;</span>
|
||
|
<span class="plain">type = LanguageDescriptors-->(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 & (LanguageDescriptors-->(x+1));</span>
|
||
|
|
||
|
<span class="plain">if (type == POSSESS_PK) {</span>
|
||
|
<span class="plain">cto = LanguageDescriptors-->(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 > 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 && 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>§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-->index == wd) return wd;</span>
|
||
|
<span class="plain">if ((line_token-->index)->0 & $20 == 0) return -1;</span>
|
||
|
<span class="plain">do {</span>
|
||
|
<span class="plain">if (line_tdata-->index == wd) return wd;</span>
|
||
|
<span class="plain">index++;</span>
|
||
|
<span class="plain">} until ((line_token-->index == ENDIT_TOKEN) || (((line_token-->index)->0 & $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>§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>§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" — usually a location and then the actor who is
|
||
|
looking — 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 >= 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 & OTHER_BIT) print "other ";</span>
|
||
|
<span class="plain">if (indef_type & MY_BIT) print "my ";</span>
|
||
|
<span class="plain">if (indef_type & THAT_BIT) print "that ";</span>
|
||
|
<span class="plain">if (indef_type & PLURAL_BIT) print "plural ";</span>
|
||
|
<span class="plain">if (indef_type & LIT_BIT) print "lit ";</span>
|
||
|
<span class="plain">if (indef_type & 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 >= 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 <= num_words) {</span>
|
||
|
<span class="plain">if (number_matched == 1) {</span>
|
||
|
<span class="plain">i=match_list-->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 <= 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-->0;</span>
|
||
|
<span class="plain">if (indef_mode == 1 && indef_type & 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)) &&</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 > 1) {</span>
|
||
|
<span class="plain">i = true;</span>
|
||
|
<span class="plain">if (number_matched > 1)</span>
|
||
|
<span class="plain">for (j=0 : j<number_matched-1 : j++)</span>
|
||
|
<span class="plain">if (Identical(match_list-->j, match_list-->(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-->pcount = i;</span>
|
||
|
<span class="plain">return i;</span>
|
||
|
<span class="plain">}</span>
|
||
|
|
||
|
<span class="plain">if (dont_ask) return match_list-->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 > 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<=number_of_classes : i++) {</span>
|
||
|
<span class="plain">while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i))</span>
|
||
|
<span class="plain">marker++;</span>
|
||
|
<span class="plain">if (match_list-->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<=number_of_classes : i++) {</span>
|
||
|
<span class="plain">while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++;</span>
|
||
|
<span class="plain">k = match_list-->marker;</span>
|
||
|
|
||
|
<span class="plain">if (match_classes-->marker > 0) print (the) k; else print (a) k;</span>
|
||
|
|
||
|
<span class="plain">if (i < j-1) print ", ";</span>
|
||
|
<span class="plain">if (i == j-1) {</span>
|
||
|
<span class="plain">if (TEMPLATE_CONFIGURATION_BITMAP & 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<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';</span>
|
||
|
<span class="plain">#Endif; ! TARGET_ZCODE</span>
|
||
|
<span class="plain">answer_words=Keyboard(buffer2, parse2);</span>
|
||
|
|
||
|
<span class="plain">! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.</span>
|
||
|
<span class="plain">first_word = (parse2-->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-->0;</span>
|
||
|
<span class="plain">for (i=0 : i<number_matched && l+i<MATCH_LIST_WORDS : i++) {</span>
|
||
|
<span class="plain">k = match_list-->i;</span>
|
||
|
<span class="plain">multiple_object-->(i+1+l) = k;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">multiple_object-->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<=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->#dict_par1;</span>
|
||
|
<span class="plain">if ((0 ~= j&1) && ~~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, > take red button</span>
|
||
|
<span class="plain">! Which one, ...</span>
|
||
|
<span class="plain">! > 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->1+1;</span>
|
||
|
<span class="plain">for (j=buffer + buffer->0 - 1 : j>=buffer+k+l : j--) j->0 = 0->(j-l);</span>
|
||
|
<span class="plain">for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(2+i);</span>
|
||
|
<span class="plain">buffer->(k+l-1) = ' ';</span>
|
||
|
<span class="plain">buffer->1 = buffer->1 + l;</span>
|
||
|
<span class="plain">if (buffer->1 >= (buffer->0 - 1)) buffer->1 = buffer->0;</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">k = WordAddress(match_from) - buffer;</span>
|
||
|
<span class="plain">l = (buffer2-->0) + 1;</span>
|
||
|
<span class="plain">for (j=buffer+INPUT_BUFFER_LEN-1 : j>=buffer+k+l : j--) j->0 = j->(-l);</span>
|
||
|
<span class="plain">for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(WORDSIZE+i);</span>
|
||
|
<span class="plain">buffer->(k+l-1) = ' ';</span>
|
||
|
<span class="plain">buffer-->0 = buffer-->0 + l;</span>
|
||
|
<span class="plain">if (buffer-->0 > (INPUT_BUFFER_LEN-WORDSIZE)) buffer-->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-->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<INPUT_BUFFER_LEN : i++) buffer2->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<=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-->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->#dict_par1;</span>
|
||
|
<span class="plain">if ((0 ~= j&1) && ~~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<pcount : j++) {</span>
|
||
|
<span class="plain">if (pattern-->j == PATTERN_NULL) continue;</span>
|
||
|
<span class="plain">#Ifdef TARGET_ZCODE;</span>
|
||
|
<span class="plain">i = 2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">i = WORDSIZE + buffer-->0;</span>
|
||
|
<span class="plain">(buffer-->0)++; buffer->(i++) = ' ';</span>
|
||
|
<span class="plain">#Endif; ! TARGET_</span>
|
||
|
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 5)</span>
|
||
|
<span class="plain">print "[Gluing in inference with pattern code ", pattern-->j, "]^";</span>
|
||
|
<span class="plain">#Endif; ! DEBUG</span>
|
||
|
|
||
|
<span class="plain">! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.</span>
|
||
|
|
||
|
<span class="plain">parse2-->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-->j >= 2 && pattern-->j < REPARSE_CODE) {</span>
|
||
|
<span class="plain">PronounNotice(pattern-->j);</span>
|
||
|
<span class="plain">for (k=1 : k<=LanguagePronouns-->0 : k=k+3)</span>
|
||
|
<span class="plain">if (pattern-->j == LanguagePronouns-->(k+2)) {</span>
|
||
|
<span class="plain">parse2-->1 = LanguagePronouns-->k;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 5)</span>
|
||
|
<span class="plain">print "[Using pronoun '", (address) parse2-->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-->1 = VM_NumberToDictionaryAddress(pattern-->j - REPARSE_CODE);</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 5)</span>
|
||
|
<span class="plain">print "[Using preposition '", (address) parse2-->1, "']^";</span>
|
||
|
<span class="plain">#Endif; ! DEBUG</span>
|
||
|
<span class="plain">}</span>
|
||
|
|
||
|
<span class="plain">! parse2-->1 now holds the dictionary address of the word to glue in.</span>
|
||
|
|
||
|
<span class="plain">if (parse2-->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-->1;</span>
|
||
|
<span class="plain">@output_stream -3;</span>
|
||
|
<span class="plain">k = k-->0;</span>
|
||
|
<span class="plain">for (l=i : l<i+k : l++) buffer->l = buffer->(l+2);</span>
|
||
|
<span class="plain">i = i + k; buffer->1 = i-2;</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">k = Glulx_PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1);</span>
|
||
|
<span class="plain">i = i + k; buffer-->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->1; (buffer->1)++; buffer->(i++) = ' ';</span>
|
||
|
<span class="plain">for (j=0 : j<buffer2->1 : i++,j++) {</span>
|
||
|
<span class="plain">buffer->i = buffer2->(j+2);</span>
|
||
|
<span class="plain">(buffer->1)++;</span>
|
||
|
<span class="plain">if (buffer->1 == INPUT_BUFFER_LEN) break;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">i = WORDSIZE + buffer-->0;</span>
|
||
|
<span class="plain">(buffer-->0)++; buffer->(i++) = ' ';</span>
|
||
|
<span class="plain">for (j=0 : j<buffer2-->0 : i++,j++) {</span>
|
||
|
<span class="plain">buffer->i = buffer2->(j+WORDSIZE);</span>
|
||
|
<span class="plain">(buffer-->0)++;</span>
|
||
|
<span class="plain">if (buffer-->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<INPUT_BUFFER_LEN : i++) buffer->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>§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 — 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 — 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 >= 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 & OTHER_BIT) print "other ";</span>
|
||
|
<span class="plain">if (indef_type & MY_BIT) print "my ";</span>
|
||
|
<span class="plain">if (indef_type & THAT_BIT) print "that ";</span>
|
||
|
<span class="plain">if (indef_type & PLURAL_BIT) print "plural ";</span>
|
||
|
<span class="plain">if (indef_type & LIT_BIT) print "lit ";</span>
|
||
|
<span class="plain">if (indef_type & 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-->0;</span>
|
||
|
<span class="plain">for (i=0 : i<=j : i++) {</span>
|
||
|
<span class="plain">n = match_list-->i;</span>
|
||
|
<span class="plain">match_scores-->i = good_ones;</span>
|
||
|
<span class="plain">ultimate = ScopeCeiling(n);</span>
|
||
|
|
||
|
<span class="plain">if (context==HELD_TOKEN && parent(n)==actor)</span>
|
||
|
<span class="plain">{ good_ones++; last=n; }</span>
|
||
|
<span class="plain">if (context==MULTI_TOKEN && ultimate==ScopeCeiling(actor)</span>
|
||
|
<span class="plain">&& n~=actor && n hasnt concealed && n hasnt scenery)</span>
|
||
|
<span class="plain">{ good_ones++; last=n; }</span>
|
||
|
<span class="plain">if (context==MULTIHELD_TOKEN && 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 && n~=advance_warning)</span>
|
||
|
<span class="plain">{ good_ones++; last=n; }</span>
|
||
|
<span class="plain">if (context==MULTIINSIDE_TOKEN && 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 && CreatureTest(n)==1)</span>
|
||
|
<span class="plain">{ good_ones++; last=n; }</span>
|
||
|
|
||
|
<span class="plain">match_scores-->i = 1000*(good_ones - match_scores-->i);</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">if (good_ones == 1) {</span>
|
||
|
<span class="plain">if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0 &&</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)) &&</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 && good_ones == 0) return match_list-->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 >= 0) {</span>
|
||
|
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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 && indef_type & 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-->0;</span>
|
||
|
<span class="plain">for (j=BestGuess(): j~=-1 && i<indef_wanted && i+offset<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 && j hasnt worn) flag = 1;</span>
|
||
|
<span class="plain">if (context == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN && parent(j) ~= actor)</span>
|
||
|
<span class="plain">flag = 0;</span>
|
||
|
|
||
|
<span class="plain">if (action_to_be == ##Take or ##Remove && 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-->(i+offset) = j;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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 >= 4) print " Rejecting it^";</span>
|
||
|
<span class="plain">#Endif; ! DEBUG</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">if (i < indef_wanted && indef_wanted < INDEF_ALL_WANTED) {</span>
|
||
|
<span class="plain">etype = TOOFEW_PE; multi_wanted = indef_wanted;</span>
|
||
|
<span class="plain">if (parser_trace >= 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-->0 = i+offset;</span>
|
||
|
<span class="plain">multi_context = context;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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<number_matched : i++) match_classes-->i = 0;</span>
|
||
|
|
||
|
<span class="plain">n = 1;</span>
|
||
|
<span class="plain">for (i=0 : i<number_matched : i++)</span>
|
||
|
<span class="plain">if (match_classes-->i == 0) {</span>
|
||
|
<span class="plain">match_classes-->i = n++; flag = 0;</span>
|
||
|
<span class="plain">for (j=i+1 : j<number_matched : j++)</span>
|
||
|
<span class="plain">if (match_classes-->j == 0 && Identical(match_list-->i, match_list-->j) == 1) {</span>
|
||
|
<span class="plain">flag=1;</span>
|
||
|
<span class="plain">match_classes-->j = match_classes-->i;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">if (flag == 1) match_classes-->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 >= 4) {</span>
|
||
|
<span class="plain">print " Grouped into ", n, " possibilities by name:^";</span>
|
||
|
<span class="plain">for (i=0 : i<number_matched : i++)</span>
|
||
|
<span class="plain">if (match_classes-->i > 0)</span>
|
||
|
<span class="plain">print " ", (The) match_list-->i, " (", match_list-->i, ") --- group ",</span>
|
||
|
<span class="plain">match_classes-->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 > 1) {</span>
|
||
|
<span class="plain">k = -1;</span>
|
||
|
<span class="plain">for (i=0 : i<number_matched : i++) {</span>
|
||
|
<span class="plain">if (match_scores-->i > k) {</span>
|
||
|
<span class="plain">k = match_scores-->i;</span>
|
||
|
<span class="plain">j = match_classes-->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-->i == k) {</span>
|
||
|
<span class="plain">if ((match_classes-->i) * (match_classes-->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 >= 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 >= 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>§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 >= 4)</span>
|
||
|
<span class="plain">print " Revising multiple object list of size ", multiple_object-->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<=multiple_object-->0 : i++) {</span>
|
||
|
<span class="plain">if ( (multi_context==MULTIEXCEPT_TOKEN && multiple_object-->i ~= second_p) ||</span>
|
||
|
<span class="plain">(multi_context==MULTIINSIDE_TOKEN && multiple_object-->i in second_p)) {</span>
|
||
|
<span class="plain">low++;</span>
|
||
|
<span class="plain">multiple_object-->low = multiple_object-->i;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">multiple_object-->0 = low;</span>
|
||
|
<span class="plain">}</span>
|
||
|
|
||
|
<span class="plain">if (multi_context == MULTI_TOKEN && action_to_be == ##Take) {</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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<=multiple_object-->0 : i++) {</span>
|
||
|
<span class="plain">if (ScopeCeiling(multiple_object-->i) == ScopeCeiling(actor)) {</span>
|
||
|
<span class="plain">low++;</span>
|
||
|
<span class="plain">multiple_object-->low = multiple_object-->i;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">multiple_object-->0 = low;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">}</span>
|
||
|
|
||
|
<span class="plain">i = multiple_object-->0;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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>§37. Match List. </b>The match list is an array, <code class="display"><span class="extract">match_list--></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 >= 6) print " Match with quality ",quality,"^";</span>
|
||
|
<span class="plain">#Endif; ! DEBUG</span>
|
||
|
<span class="plain">if (token_filter ~= 0 && ConsultNounFilterToken(obj) == 0) {</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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 < match_length) rtrue;</span>
|
||
|
<span class="plain">if (quality > match_length) { match_length = quality; number_matched = 0; }</span>
|
||
|
<span class="plain">else {</span>
|
||
|
<span class="plain">if (number_matched >= MATCH_LIST_WORDS) rtrue;</span>
|
||
|
<span class="plain">for (i=0 : i<number_matched : i++)</span>
|
||
|
<span class="plain">if (match_list-->i == obj) rtrue;</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">match_list-->number_matched++ = obj;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 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>§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 & OTHER_BIT ~= 0) threshold++;</span>
|
||
|
<span class="plain">if (indef_type & MY_BIT ~= 0) threshold++;</span>
|
||
|
<span class="plain">if (indef_type & THAT_BIT ~= 0) threshold++;</span>
|
||
|
<span class="plain">if (indef_type & LIT_BIT ~= 0) threshold++;</span>
|
||
|
<span class="plain">if (indef_type & 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 >= 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<number_matched : i++) {</span>
|
||
|
<span class="plain">obj = match_list-->i; its_owner = parent(obj); its_score=0; met=0;</span>
|
||
|
|
||
|
<span class="plain">! if (indef_type & OTHER_BIT ~= 0</span>
|
||
|
<span class="plain">! && obj ~= itobj or himobj or herobj) met++;</span>
|
||
|
<span class="plain">if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++;</span>
|
||
|
<span class="plain">if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++;</span>
|
||
|
<span class="plain">if (indef_type & LIT_BIT ~= 0 && obj has light) met++;</span>
|
||
|
<span class="plain">if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++;</span>
|
||
|
<span class="plain">if (indef_owner ~= 0 && its_owner == indef_owner) met++;</span>
|
||
|
|
||
|
<span class="plain">if (met < threshold) {</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 4)</span>
|
||
|
<span class="plain">print " ", (The) match_list-->i, " (", match_list-->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-->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 & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))</span>
|
||
|
<span class="plain">its_score = its_score + SCORE__GNA;</span>
|
||
|
|
||
|
<span class="plain">match_scores-->i = match_scores-->i + its_score;</span>
|
||
|
<span class="plain">#Ifdef DEBUG;</span>
|
||
|
<span class="plain">if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i,</span>
|
||
|
<span class="plain">") in ", (the) its_owner, " : ", match_scores-->i, " points^";</span>
|
||
|
<span class="plain">#Endif; ! DEBUG</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">}</span>
|
||
|
|
||
|
<span class="plain">for (i=0 : i<number_matched : i++) {</span>
|
||
|
<span class="plain">while (match_list-->i == -1) {</span>
|
||
|
<span class="plain">if (i == number_matched-1) { number_matched--; break; }</span>
|
||
|
<span class="plain">for (j=i : j<number_matched-1 : j++) {</span>
|
||
|
<span class="plain">match_list-->j = match_list-->(j+1);</span>
|
||
|
<span class="plain">match_scores-->j = match_scores-->(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>§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<number_matched : i++) {</span>
|
||
|
<span class="plain">if (match_list-->i >= 0) {</span>
|
||
|
<span class="plain">its_score = match_scores-->i;</span>
|
||
|
<span class="plain">if (its_score > 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 >= 4)</span>
|
||
|
<span class="plain">if (best < 0) print " Best guess ran out of choices^";</span>
|
||
|
<span class="plain">else print " Best guess ", (the) match_list-->earliest,</span>
|
||
|
<span class="plain">" (", match_list-->earliest, ")^";</span>
|
||
|
<span class="plain">#Endif; ! DEBUG</span>
|
||
|
<span class="plain">if (best < 0) return -1;</span>
|
||
|
<span class="plain">i = match_list-->earliest;</span>
|
||
|
<span class="plain">match_list-->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>§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<number_matched : i++) {</span>
|
||
|
<span class="plain">its_score = match_scores-->i;</span>
|
||
|
<span class="plain">if (its_score == best) earliest = -1;</span>
|
||
|
<span class="plain">if (its_score > best) { best = its_score; earliest = match_list-->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>§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 >=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.&1; n1 = (o1.#1)/WORDSIZE;</span>
|
||
|
<span class="plain">p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;</span>
|
||
|
|
||
|
<span class="plain">! for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;</span>
|
||
|
<span class="plain">! for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;</span>
|
||
|
|
||
|
<span class="plain">for (i=0 : i<n1 : i++) {</span>
|
||
|
<span class="plain">flag = 0;</span>
|
||
|
<span class="plain">for (j=0 : j<n2 : j++)</span>
|
||
|
<span class="plain">if (p1-->i == p2-->j) flag = 1;</span>
|
||
|
<span class="plain">if (flag == 0) rfalse;</span>
|
||
|
<span class="plain">}</span>
|
||
|
|
||
|
<span class="plain">for (j=0 : j<n2 : j++) {</span>
|
||
|
<span class="plain">flag = 0;</span>
|
||
|
<span class="plain">for (i=0 : i<n1 : i++)</span>
|
||
|
<span class="plain">if (p1-->i == p2-->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>§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> — 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) && (from == pcount-1) &&</span>
|
||
|
<span class="plain">(pattern-->from > 1) && (pattern-->from < 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-->from);</span>
|
||
|
<span class="plain">if (ForActivity(CLARIFYING_PARSERS_CHOICE_ACT, pattern-->from) == 0) {</span>
|
||
|
<span class="plain">print "("; PrintCommand(from); print ")^";</span>
|
||
|
<span class="plain">}</span>
|
||
|
<span class="plain">EndActivity(CLARIFYING_PARSERS_CHOICE_ACT, pattern-->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<pcount : k++) {</span>
|
||
|
<span class="plain">i = pattern-->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 >= 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>§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-->(pcount) ~= ENDIT_TOKEN)) {</span>
|
||
|
<span class="plain">if (pcount > 0) AnalyseToken(line_token-->(pcount-1));</span>
|
||
|
<span class="plain">if ((pcount > 0) && (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 > 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>§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-->0;</span>
|
||
|
<span class="plain">if (i == MATCH_LIST_WORDS-1) { toomany_flag = 1; rtrue; }</span>
|
||
|
<span class="plain">for (j=1 : j<=i : j++)</span>
|
||
|
<span class="plain">if (o == multiple_object-->j) rtrue;</span>
|
||
|
<span class="plain">i++;</span>
|
||
|
<span class="plain">multiple_object-->i = o;</span>
|
||
|
<span class="plain">multiple_object-->0 = i;</span>
|
||
|
<span class="plain">];</span>
|
||
|
|
||
|
<span class="plain">[ MultiSub o i j k;</span>
|
||
|
<span class="plain">i = multiple_object-->0;</span>
|
||
|
<span class="plain">for (j=1 : j<=i : j++)</span>
|
||
|
<span class="plain">if (o == multiple_object-->j) {</span>
|
||
|
<span class="plain">for (k=j : k<=i : k++) multiple_object-->k = multiple_object-->(k+1);</span>
|
||
|
<span class="plain">multiple_object-->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-->0;</span>
|
||
|
<span class="plain">for (j=1 : j<=i : j++) {</span>
|
||
|
<span class="plain">o = multiple_object-->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>§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 — 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 — 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 >= 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>§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>§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 >= 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) && (context == MULTIINSIDE_TOKEN) &&</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) && (context ~= CREATURE_TOKEN) &&</span>
|
||
|
<span class="plain">(indef_mode == 0) && (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>§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>§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) &&</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.&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-->0, top_object) > 0);</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">i = (((ad-->0)->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)<n : i++)</span>
|
||
|
<span class="plain">if (ad-->i)</span>
|
||
|
<span class="plain">DoScopeActionAndRecurse(ad-->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>§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 >= 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>§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 && ConsultNounFilterToken(item) == 0) return;</span>
|
||
|
|
||
|
<span class="plain">if (match_from <= 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) && (player == item)) MakeMatch(item, 1); ! "me"</span>
|
||
|
<span class="plain">if ((i >= 2) && (i < 128) && (LanguagePronouns-->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) > 0)</span>
|
||
|
<span class="plain">if (indef_nspec_at > 0 && 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) > 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>§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 >= 5) print " Trying ", (the) obj, " (", obj, ") at word ", wn, "^";</span>
|
||
|
<span class="plain">#Endif; ! DEBUG</span>
|
||
|
|
||
|
<span class="plain">if (nomatch && 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 > 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 >= 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 > 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 & 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 >= 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 && player == obj) { k=1; jump MMbyPN; }</span>
|
||
|
|
||
|
<span class="plain">if (w >= 2 && w < 128 && (LanguagePronouns-->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->#dict_par1) & $$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->#dict_par1) & $$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>§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 >= 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 >= 256) {</span>
|
||
|
<span class="plain">k = indirect(parser_inflection, obj, wd);</span>
|
||
|
<span class="plain">if (k >= 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.&m; l = (obj.#m)/WORDSIZE-1;</span>
|
||
|
<span class="plain">for (m=0 : m<=l : m++)</span>
|
||
|
<span class="plain">if (wd == k-->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.&prop; l = (obj.#prop)/WORDSIZE-1;</span>
|
||
|
<span class="plain">for (m=0 : m<=l : m++)</span>
|
||
|
<span class="plain">if (wd == k-->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>§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-->0;</span>
|
||
|
<span class="plain">for (j=1 : j<=s : j=j+3)</span>
|
||
|
<span class="plain">if (i == LanguagePronouns-->j)</span>
|
||
|
<span class="plain">return j+2;</span>
|
||
|
<span class="plain">if ((i->#dict_par1)&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>§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 >= 1) return j;</span>
|
||
|
|
||
|
<span class="plain">#Ifdef TARGET_ZCODE;</span>
|
||
|
<span class="plain">i = wordnum*4+1; j = parse->i; num = j+buffer; len = parse->(i-1);</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX</span>
|
||
|
<span class="plain">i = wordnum*3; j = parse-->i; num = j+buffer; len = parse-->(i-1);</span>
|
||
|
<span class="plain">#Endif; ! TARGET_</span>
|
||
|
|
||
|
<span class="plain">if (len >= 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<=len : c++) {</span>
|
||
|
<span class="plain">digit=num->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 > 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>§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 — gender/noun/animation combinations — 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>§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<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->#dict_par1) & $$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>§58. Pronoun Handling. </b></p>
|
||
|
|
||
|
|
||
|
<pre class="display">
|
||
|
<span class="plain">[ SetPronoun dword value x;</span>
|
||
|
<span class="plain">for (x=1 : x<=LanguagePronouns-->0 : x=x+3)</span>
|
||
|
<span class="plain">if (LanguagePronouns-->x == dword) {</span>
|
||
|
<span class="plain">LanguagePronouns-->(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<=LanguagePronouns-->0 : x=x+3)</span>
|
||
|
<span class="plain">if (LanguagePronouns-->x == dword)</span>
|
||
|
<span class="plain">return LanguagePronouns-->(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-->g;</span>
|
||
|
<span class="plain">for (x=1 : x<=LanguagePronouns-->0 : x=x+3)</span>
|
||
|
<span class="plain">if (bm & (LanguagePronouns-->(x+1)) ~= 0)</span>
|
||
|
<span class="plain">LanguagePronouns-->(x+2) = obj;</span>
|
||
|
|
||
|
<span class="plain">if (((g % 6) < 3) && (obj has ambigpluralname)) {</span>
|
||
|
<span class="plain">g = g + 3;</span>
|
||
|
<span class="plain">bm = PowersOfTwo_TB-->g;</span>
|
||
|
<span class="plain">for (x=1 : x<=LanguagePronouns-->0 : x=x+3)</span>
|
||
|
<span class="plain">if (bm & (LanguagePronouns-->(x+1)) ~= 0)</span>
|
||
|
<span class="plain">LanguagePronouns-->(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>§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->1;</span>
|
||
|
<span class="plain">#Ifnot; ! TARGET_GLULX;</span>
|
||
|
<span class="plain">if (location ~= nothing && parent(player) ~= nothing) DrawStatusLine();</span>
|
||
|
<span class="plain">KeyboardPrimitive(buffer2, parse2);</span>
|
||
|
<span class="plain">j = parse2-->0;</span>
|
||
|
<span class="plain">#Endif; ! TARGET_</span>
|
||
|
<span class="plain">if (j) { ! at least one word entered</span>
|
||
|
<span class="plain">i = parse2-->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 "> ";</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>§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-->0;</span>
|
||
|
<span class="plain">for (i=1 : i<=n : i=i+2)</span>
|
||
|
<span class="plain">if (o == LanguageNumbers-->i) return LanguageNumbers-->(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>§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 — 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 --> (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<words: i++)</span>
|
||
|
<span class="plain">to-->i = from-->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-->0;</span>
|
||
|
<span class="plain">alt_match_list-->0 = x;</span>
|
||
|
<span class="plain">! swap the values</span>
|
||
|
<span class="plain">if (x < number_matched) x = number_matched;</span>
|
||
|
<span class="plain">for (i=x: i>0: i--) {</span>
|
||
|
<span class="plain">x = match_list-->(i-1);</span>
|
||
|
<span class="plain">match_list-->(i-1) = alt_match_list-->i;</span>
|
||
|
<span class="plain">alt_match_list-->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<2) rfalse;</span>
|
||
|
|
||
|
<span class="plain">if (cobj_flag == 1) {</span>
|
||
|
<span class="plain">.CodeOne;</span>
|
||
|
<span class="plain">if (parameters > 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-->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-->0, " combinations]^";</span>
|
||
|
<span class="plain">#endif;</span>
|
||
|
<span class="plain">l = 0;</span>
|
||
|
<span class="plain">for (i=1: i<=alt_match_list-->0: i++) {</span>
|
||
|
<span class="plain">spcount = ScoreDabCombo(obj, alt_match_list-->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>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 > 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-->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-->pcount == PREPOSITION_TT) pcount++;</span>
|
||
|
<span class="plain">if (line_ttype-->pcount == ELEMENTARY_TT) {</span>
|
||
|
<span class="plain">if (line_tdata-->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 <= 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) && (l->#dict_par1) &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-->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-->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, " => ", 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>§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>§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 — a text — 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-->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<=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->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>§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">.&</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.&prop == 0) && (prop >= 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>§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-->0; p = TEXT_TY_Temporarily_Transmute(from_txt);</span>
|
||
|
<span class="plain">len = TEXT_TY_CharacterLength(from_txt);</span>
|
||
|
<span class="plain">if (len > 118) len = 118;</span>
|
||
|
<span class="plain">#ifdef TARGET_ZCODE;</span>
|
||
|
<span class="plain">buffer->1 = len; at = 2;</span>
|
||
|
<span class="plain">#ifnot;</span>
|
||
|
<span class="plain">buffer-->0 = len; at = 4;</span>
|
||
|
<span class="plain">#endif;</span>
|
||
|
<span class="plain">for (i=0:i<len:i++) buffer->(i+at) = CharToCase(BlkValueRead(from_txt, i), 0);</span>
|
||
|
<span class="plain">for (:at+i<120:i++) buffer->(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>§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-->0;</span>
|
||
|
<span class="plain">LIST_OF_TY_SetLength(list, len);</span>
|
||
|
<span class="plain">for (i=1: i<=len: i++)</span>
|
||
|
<span class="plain">LIST_OF_TY_PutItem(list, i, multiple_object-->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 > 63) len = 63;</span>
|
||
|
<span class="plain">multiple_object-->0 = len;</span>
|
||
|
<span class="plain">for (i=1: i<=len: i++)</span>
|
||
|
<span class="plain">multiple_object-->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>
|
||
|
|