! Positions.h
! Version 1.1
! An Inform 6 extension by Nathan Schwartzman
! Feedback to [email protected]
!
! #Include between "Parser" and "Verblib".
! Before #Including "Parser", add the line
! "Replace TrygivenObject".
!
! This extension will allow input of the following type:
!
! TAKE THE BRASS KEY FROM THE POUCH IN THE TREASURE CHEST
!
! PUT EVERYTHING IN THE TOASTER ON THE TABLE
!
! This does not require you to alter existing
! code at all, unless you already have a replacement
! for TryGivenObject() for some reason.
!
! What doesn't work:
! PUT ALL IN CHEST ON CHAIR (may work by fiddling with
!  grammar lines)
! Pronouns (e.g. X IT IN CHEST)
!
! Credit goes to Anson Turner and his Platypus library
! for the idea for this; I also took some of his code.
! I hope he doesn't mind.
!
! Version 1.1 fixes a bug and allows the use of articles
! and descriptors.

Global indef_specific;

[ TryGivenObject obj flag threshold k w j p;

   #Ifdef DEBUG;
   if (parser_trace >= 5) print "    Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
   #Endif; ! DEBUG

   dict_flags_of_noun = 0;

!  If input has run out then always match, with only quality 0 (this saves
!  time).

   if (wn > num_words) {
       if (indef_mode ~= 0)
           dict_flags_of_noun = $$01110000;  ! Reject "plural" bit
       MakeMatch(obj,0);
       #Ifdef DEBUG;
       if (parser_trace >= 5) print "    Matched (0)^";
       #Endif; ! DEBUG
       return 1;
   }

!  Ask the object to parse itself if necessary, sitting up and taking notice
!  if it says the plural was used:

   if (obj.parse_name~=0) {
       parser_action = NULL; j=wn;
       k = RunRoutines(obj,parse_name);
       if (k > 0) {
           wn=j+k;

         .MMbyPN;

               ! The next two lines take care of position parsing.

               p = AppendPreps(obj);
               if (p) { wn = wn + p; k = k + p; }

           if (parser_action == ##PluralFound)
               dict_flags_of_noun = dict_flags_of_noun | 4;

           if (dict_flags_of_noun & 4) {
               if (~~allow_plurals) k = 0;
               else {
                   if (indef_mode == 0) {
                       indef_mode = 1; indef_type = 0; indef_wanted = 0;
                   }
                   indef_type = indef_type | PLURAL_BIT;
                   if (indef_wanted == 0) indef_wanted = 100;
               }
           }

           #Ifdef DEBUG;
           if (parser_trace >= 5) print "    Matched (", k, ")^";
           #Endif; ! DEBUG
           if (~~flag) MakeMatch(obj,k);
           return k;
       }
       if (k == 0) jump NoWordsMatch;
   }

   ! The default algorithm is simply to count up how many words pass the
   ! Refers test:

   parser_action = NULL;

   w = NounWord();

   if (w == 1 && player == obj) { k=1; jump MMbyPN; }

   if (w >= 2 && w < 128 && (LanguagePronouns-->w == obj)) { k = 1; jump MMbyPN; }

   j=--wn;
   threshold = ParseNoun(obj);
   #Ifdef DEBUG;
   if (threshold >= 0 && parser_trace >= 5) print "    ParseNoun returned ", threshold, "^";
   #Endif; ! DEBUG
   if (threshold < 0) wn++;
   if (threshold > 0) { k = threshold; jump MMbyPN; }

   if (threshold == 0 || Refers(obj,wn-1) == 0) {
     .NoWordsMatch;
       if (indef_mode ~= 0) {
           k = 0; parser_action = NULL;
           jump MMbyPN;
       }
       rfalse;
   }

   if (threshold < 0) {
       threshold = 1;
       dict_flags_of_noun = (w->#dict_par1) & $$01110100;
       w = NextWord();
       while (Refers(obj, wn-1)) {
           threshold++;
           if (w)
              dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & $$01110100);
           w = NextWord();
       }
   }

   k = threshold;
   jump MMbyPN;
];

[ AppendPreps obj wd i m ap z x;

       ! Here we handle the case of PUT THE DAGGER IN THE SHEATH ON MY BELT.

       if (~~parent(obj)) rfalse;

       wn--; wd = NextWord();

       if ((parent(obj) has container && wd == 'in' or 'inside' or 'within' or 'from')
               || (parent(obj) has supporter && wd == 'on' or 'upon' or 'off' or 'from')) {

           ! Are we sure we have PUT (DAGGER) IN (SHEATH) rather than PUT (DAGGER IN SHEATH)?

               for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
               if (NextWord() == LanguageDescriptors-->x) {
                               m++;
                       }
                       else wn--;

           if (line_ttype-->pcount == PREPOSITION_TT)
           {   z = pcount;
               if (WordLeft(line_tdata-->z)) jump Prepositions2;
               if ((line_token-->z)->0 & $20)
               {   do
                   {   if (WordLeft(line_tdata-->z)) jump Prepositions2;
                       z++;
                   } until ((line_token-->z == ENDIT_TOKEN)
                             || (((line_token-->z)->0 & $10) == 0));
               }
               jump GiveResult;
           }

               ! Ok, now we're sure.

           .Prepositions2;

               ap = allow_plurals;
               i = TryGivenObject(parent(obj), 1);
               allow_plurals = ap;
               if (~~i) jump GiveResult;

               m = m + i + 1;
               obj = parent(obj);
         }

       .GiveResult;

       if (i == 0) rfalse;
       if (indef_wanted == 100) {
               if (parent(obj) has container) indef_specific = 1;
               if (parent(obj) has supporter) indef_specific = 2;
       }
       return m;
];

[ WordLeft w     own cw flag;
   own = wn;
   while ((cw = NextWordStopped()) ~= -1)
   {   if (flag)
       {   if (cw && (cw->#dict_par1) & 1) break;
           flag = 0;
       }
       if (cw == w) { wn = own; rtrue; }
       if (IsALinkWord(w)) flag = 1;
   }
   wn = own;
   rfalse;
];

[ IsALinkWord wd;
   if (wd == COMMA_WORD or AND1__WD or AND2__WD or AND3__WD
       or THEN1__WD or THEN2__WD or THEN3__WD)
       rtrue;
   rfalse;
];