! NounDomain 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: ! ! 0 if no match at all could be made, ! 1 if a multiple object was made, ! k if object k was the one decided upon, ! REPARSE_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. ! ! In the case when it returns 1=4) { print " [NounDomain called at word ", wn, "^"; print " "; if (indef_mode) { print "seeking indefinite object: "; if (indef_type & OTHER_BIT) print "other "; if (indef_type & MY_BIT) print "my "; if (indef_type & THAT_BIT) print "that "; if (indef_type & PLURAL_BIT) print "plural "; if (indef_type & LIT_BIT) print "lit "; if (indef_type & UNLIT_BIT) print "unlit "; if (indef_owner ~= 0) print "owner:", (name) indef_owner; new_line; print " number wanted: "; if (indef_wanted == 100) print "all"; else print indef_wanted; new_line; print " most likely GNAs of names: ", indef_cases, "^"; } else print "seeking definite object^"; } #endif; match_length=0; number_matched=0; match_from=wn; placed_in_flag=0; SearchScope(domain1, domain2, context); #ifdef DEBUG; if (parser_trace>=4) print " [ND made ", number_matched, " matches]^"; #endif; wn=match_from+match_length; ! If nothing worked at all, leave with the word marker skipped past the ! first unmatched word... if (number_matched==0) { wn++; rfalse; } ! Suppose that there really were some words being parsed (i.e., we did ! not just infer). If so, and if there was only one match, it must be ! right and we return it... if (match_from <= num_words) { if (number_matched==1) { i=match_list-->0; return i; } ! ...now suppose that there was more typing to come, i.e. suppose that ! the user entered something beyond this noun. If nothing ought to follow, ! then there must be a mistake, (unless what does follow is just a full ! stop, and or comma) if (wn<=num_words) { i=NextWord(); wn--; if (i ~= AND1__WD or AND2__WD or AND3__WD or comma_word or THEN1__WD or THEN2__WD or THEN3__WD or BUT1__WD or BUT2__WD or BUT3__WD) { if (lookahead==ENDIT_TOKEN) rfalse; } } } ! Now look for a good choice, if there's more than one choice... number_of_classes=0; if (number_matched==1) i=match_list-->0; if (number_matched>1) { i=Adjudicate(context); if (i==-1) rfalse; if (i==1) rtrue; ! Adjudicate has made a multiple ! object, and we pass it on } ! If i is non-zero here, one of two things is happening: either ! (a) an inference has been successfully made that object i is ! the intended one from the user's specification, or ! (b) the user finished typing some time ago, but we've decided ! on i because it's the only possible choice. ! In either case we have to keep the pattern up to date, ! note that an inference has been made and return. ! (Except, we don't note which of a pile of identical objects.) if (i~=0) { if (dont_infer) return i; if (inferfrom==0) inferfrom=pcount; pattern-->pcount = i; return i; } ! If we get here, there was no obvious choice of object to make. If in ! fact we've already gone past the end of the player's typing (which ! means the match list must contain every object in scope, regardless ! of its name), then it's foolish to give an enormous list to choose ! from - instead we go and ask a more suitable question... if (match_from > num_words) jump Incomplete; ! Now we print up the question, using the equivalence classes as worked ! out by Adjudicate() so as not to repeat ourselves on plural objects... if (context==CREATURE_TOKEN) L__M(##Miscellany, 45); else L__M(##Miscellany, 46); j=number_of_classes; marker=0; for (i=1:i<=number_of_classes:i++) { while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++; k=match_list-->marker; if (match_classes-->marker > 0) print (the) k; else print (a) k; if (ii=' '; answer_words=Keyboard(buffer2, parse2); first_word=(parse2-->1); ! Take care of "all", because that does something too clever here to do ! later on: if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) { if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) { l=multiple_object-->0; for (i=0:ii; multiple_object-->(i+1+l) = k; } multiple_object-->0 = i+l; rtrue; } L__M(##Miscellany, 47); jump WhichOne; } ! If the first word of the reply can be interpreted as a verb, then ! assume that the player has ignored the question and given a new ! command altogether. ! (This is one time when it's convenient that the directions are ! not themselves verbs - thus, "north" as a reply to "Which, the north ! or south door" is not treated as a fresh command but as an answer.) #ifdef LanguageIsVerb; if (first_word==0) { j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j; } #endif; if (first_word ~= 0) { j=first_word->#dict_par1; if ((0~=j&1) && (first_word ~= 'long' or 'short' or 'normal' or 'brief' or 'full' or 'verbose')) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } } ! Now we insert the answer into the original typed command, as ! words additionally describing the same object ! (eg, > take red button ! Which one, ... ! > music ! becomes "take music red button". The parser will thus have three ! words to work from next time, not two.) k = WordAddress(match_from) - buffer; l=buffer2->1+1; for (j=buffer + buffer->0 - 1: j>= buffer+k+l: j--) j->0 = 0->(j-l); for (i=0:i(k+i) = buffer2->(2+i); buffer->(k+l-1) = ' '; buffer->1 = buffer->1 + l; if (buffer->1 >= (buffer->0 - 1)) buffer->1 = buffer->0; ! Having reconstructed the input, we warn the parser accordingly ! and get out. return REPARSE_CODE; ! Now we come to the question asked when the input has run out ! and can't easily be guessed (eg, the player typed "take" and there ! were plenty of things which might have been meant). .Incomplete; if (context==CREATURE_TOKEN) L__M(##Miscellany, 48); else L__M(##Miscellany, 49); for (i=2:i<120:i++) buffer2->i=' '; answer_words=Keyboard(buffer2, parse2); first_word=(parse2-->1); #ifdef LanguageIsVerb; if (first_word==0) { j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j; } #endif; ! Once again, if the reply looks like a command, give it to the ! parser to get on with and forget about the question... if (first_word ~= 0) { j=first_word->#dict_par1; if (0~=j&1) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } } ! ...but if we have a genuine answer, then: ! ! (1) we must glue in text suitable for anything that's been inferred. if (inferfrom ~= 0) { for (j = inferfrom: jj == PATTERN_NULL) continue; i=2+buffer->1; (buffer->1)++; buffer->(i++) = ' '; if (parser_trace >= 5) print "[Gluing in inference with pattern code ", pattern-->j, "]^"; parse2-->1 = 0; ! An inferred object. Best we can do is glue in a pronoun. ! (This is imperfect, but it's very seldom needed anyway.) if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE) { PronounNotice(pattern-->j); for (k=1: k<=LanguagePronouns-->0: k=k+3) if (pattern-->j == LanguagePronouns-->(k+2)) { parse2-->1 = LanguagePronouns-->k; if (parser_trace >= 5) print "[Using pronoun '", (address) parse2-->1, "']^"; break; } } else { ! An inferred preposition. parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE); if (parser_trace >= 5) print "[Using preposition '", (address) parse2-->1, "']^"; } ! parse2-->1 now holds the dictionary address of the word to glue in. if (parse2-->1 ~= 0) { k = buffer + i; @output_stream 3 k; print (address) parse2-->1; @output_stream -3; k = k-->0; for (l=i:ll = buffer->(l+2); i = i + k; buffer->1 = i-2; } } } ! (2) we must glue the newly-typed text onto the end. i=2+buffer->1; (buffer->1)++; buffer->(i++) = ' '; for (j=0: j1: i++, j++) { buffer->i = buffer2->(j+2); (buffer->1)++; if (buffer->1 == 120) break; } ! (3) we fill up the buffer with spaces, which is unnecessary, but may ! help incorrectly-written interpreters to cope. for (:i<120:i++) buffer->i = ' '; return REPARSE_CODE; ];