! QPrint
! Mid-string rule printing system.
!  Dave Robinson
!  khelwood at hotmail dot com
! version 1: 17-03-2005

! Rules are inserted into strings using % as an escape character.
!  For instance,
!   QPrint("You take %the 1% and put %it 1% into %the 2%.",obj1,obj2);
!   is equivalent to
!   print "You take ",(the) obj1," and put ", (itorthem) obj1,
!      " into ",(the) obj2,".";
!  The number 1 to 6 indicated which argument to use.
!  For instance,
!   QPrint("%The 1%,x,y,z);
!  translates to print (The) x;
!   whereas
!   QPrint("%The 3%",x,y,z);
!  translates to print (The) z;
! You can also use "noun" and "second" as argument indicators.
!  For instance
!   QPrint("You cannot take %the noun%.");
!   or
!   QPrint("You put %the n% into %the s%.");
!  n is short for "noun"
!  s is short for "second"
!  a is short for "actor"

! Use examples:
!  QPrint("You take %t n% and put %it n% in your pocket.");
!  QPrint("%T n% %is n% too large to carry.");
!  QPrint("You put %t 1% into %t 2%.", obj1, obj2);
!  QPrint("There %is 1% %a 1% here.", x);

! Command list:
!  ROUTINE:
!   QPrint(" ... %f 1 2% ... ", rout, arg);
!   This command calls function rout with argument arg.
!  ADDRESS: goes like print (address) ...
!   QPrint("The verb_word is '%addr 1%'.", verb_word);
!   The figure 1 in the command means "first argument",
!   in this case, verb_word.
!  STRING: goes like print (string) ...
!   QPrint("The string says ~%str 1%~.", "Hello there");
!  CHAR: goes like print (char) ...
!   QPrint("The character is %ch 1%.", 'X');
!  NUMBER: goes like print n;
!   QPrint("The number is %num 1%.", n);
!  OBJECT: goes like print (object) ...
!   QPrint("The object's object-name is %obj 1%.", obj);
!  NAME: goes like print (name) ...
!   QPrint("The object's short name is %name 1%.", obj);
!  THE: goes like print (the) ... or print (The) ...
!   QPrint("You open %the 1%.", noun);
!   or equivalently
!   QPrint("You open %the noun%.");
!   or
!   QPrint("You open %t n%.");
!   t is short for "the" and n is short for "noun".
!   If the t is capitalised, then the printing rule (The) is used.
!   e.g. QPrint("%The 1% is open.",noun);
!   or   QPrint("%T n% is open.");
!  A: goes like print (a) noun
!   QPrint("You can see %a 1% here.", obj);
!  GEN: goes like print (the) x, "'s";
!   QPrint("%Gen 1% eyes are open.", person);
!   Basically, this prints
!    "Your" (or "your") if person==player
!    (The) person,"'s"  otherwise
!  HE: Nominative pronoun
!   QPrint("%He 1% explodes.", obj);
!   prints "You" or "He" or "They" &c.
!   If the h in %he is lower case, then
!   prints "you" or "he" or "they" &c.
!  HIM: Accusative pronoun
!   QPrint("You hug %him 1%.", obj);
!   prints "you" or "her" or "them" &c.
!  HIS: Genitive pronoun
!   QPrint("You take %his 1% pulse.", obj);
!   prints "your" or "his" or "their" &c.
!   if the h in %his is upper case then
!   prints "Your" or "His" or "Their" &c.
!  HERS:
!   QPrint("That isn't %hers 1%.", obj);
!   prints "yours" or "hers" or "theirs" &c.
!  SELF:
!   %self 1%
!   prints "yourself" or "herself" or "themselves" &c.
!  THAT:
!   %That 1%
!   prints "You" or "He" or "That" or "Those" &c.
!   %that 1%
!   prints "you" or "he" or "that" or "those" &c.
!  THATS:
!   %Thats 1%
!   prints "You're" or "She's" or "That's" or "Those are"
!   also works with lower case
!  THATSNOT:
!   %Thatsnot 1%
!   prints "You're not" or "Those aren't" &c.
!   also works with lower case
!  HES:
!   %Hes 1%
!   prints "You're" or "He's" or "They're" &c.
!   also works with lower case
!  IS:
!   QPrint("%The 1% %is 1% here.", obj);
!   prints "are" if obj is player or plural
!   prints "is" otherwise
!  HAS:
!   QPrint("The 1% %has 1% no windows.", obj);
!   prints "have" if obj is player or plural
!   prints "has" otherwise
!  DOES:
!   QPrint("%The 1% %do 1%n't reply.", obj);
!   prints "does" or "do"
!  S:
!   QPrint("%The 1% look%s 1% around.", obj);
!   prints "s" or nothing.
!   The command above may print
!   "You look around" or "Bob looks around" &c.
!  ES:
!   QPrint("The 1% focus%es 1% the telescope.", obj);
!   prints "es" or nothing.
!   The command above may print
!   "You focus the telescope" or "Bob focuses the telescope" &c.
!  INORON:
!   %in 1% or %on 1% or %In 1% or %On 1%
!   if the argument is a supporter, prints "on" or "On"
!   otherwise prints "in" or "In"
!  INORONTHE:
!   Equivalent to %in followed by %the.
!  OFFOROUTOF:
!   %off 1%
!   if the argument is a supporter prints "off"
!   otherwise prints "out of"
!  IF:
!   %if X rule Y%
!   where X indicates an argument that is considered as
!    a boolean,
!   rule indicates any rule listed here,
!   Y indicates the arguments for the rule.
!   So, for instance,
!   QPrint("%if 1 the 2%", fl, obj);
!   is equivalent to
!   if (fl) print (the) obj;
!  IFNOT:
!   %if~ X rule Y%
!   this works as above but "not"s the boolean argument.
!  QUESTION:
!   %? 1 txt1#txt2%
!   If the boolean is true, prints txt1, otherwise
!   prints txt2.
!   For instance
!  QPrint("The apple is %? 1 rotten#ripe%.", fl);
!   will print "The apple is rotten."
!   or "The apple is ripe."
!   depending on fl.
!  EMPH:
!   %emph%, e.g. QPrint("%emph%This sentence in italics.%roman%.");
!   or
!   %emph txt%, e.g. QPrint("One word is in %emph italics%.");
!  BOLD:
!   As emph but with bold instead of italics.
!  ROMAN:
!   Switches style to roman.
!  THEIS:
!   Equivalent to %the followed by %is.
!   e.g.
!   QPrint("%Theis 1% open.", obj);
!   or
!   QPrint("%Tis 1% open.", obj);
!   (where T is short for The).
!   This also works with lower case.
!  HEIS:
!   %Heis 1%
!   Equivalent to %He followed by %is.
!   Also works with lower case.
!  WAS:
!   %was 1%
!   prints "was" or "were"
!  THEWAS:
!   %Twas 1%
!   equivalent to %The followed by %was
!   Also works with lower case.
!  THATWAS
!   %Thatwas 1%
!   equivalent to %That followed by %was
!   Also works with lower case.
!  OFFTHE:
!   %offthe 1%
!   equivalent to %off followed by %the
!  HEWAS:
!   %hewas 1%
!   equivalent to %he followed by %was
!  IFSPACE:
!   %if_ 1 rule 2%
!   if followed by a space if argument 1 is true.
!  SPACEIF:
!   %_if 1 rule 2%
!   if preceded by a space if argument 1 is true.

! The routine QPR works the same as QPrint but prints
!  a new-line at the end.

System_file;

#IfNDef QPrint;

Array QPArg --> 6;
Constant MAX_QPRINT_LENGTH = 253;
#IfDef TARGET_GLULX;
Constant QP_POS_INDEX = MAX_QPRINT_LENGTH;
Array QPchar -> (MAX_QPRINT_LENGTH+1);
#IfNot;
Constant QP_POS_INDEX = MAX_QPRINT_LENGTH+2;
Array QPchar string (MAX_QPRINT_LENGTH+2);
#EndIf;
Constant QP_esc_ch = '%';
Constant QP_sep_ch = '#';

Constant QP_CAPS     = $8000; ! (-32768 - largest bit in ZCode)
Constant QP_ROUTINE  =  1; ! up to 4 parameters
Constant QP_ADDRESS  =  2; ! 1 parameter
Constant QP_STRING   =  3; ! 1
Constant QP_CHAR     =  4; ! 1
Constant QP_NUMBER   =  5; ! 1
Constant QP_OBJECT   =  6; ! 1
Constant QP_NAME     =  7; ! 2
Constant QP_THE      =  8; ! 2
Constant QP_THEACC   =  9; ! 2
Constant QP_A        = 10; ! 2
Constant QP_GEN      = 11; ! 2
Constant QP_HE       = 12; ! 1
Constant QP_HIM      = 13; ! 1
Constant QP_HIS      = 14; ! 1
Constant QP_HERS     = 15;
Constant QP_SELF     = 16; ! 1
Constant QP_THAT     = 17; ! 1
Constant QP_THATACC  = 18; ! 1
Constant QP_THATS    = 19; ! 1
Constant QP_THATSNOT = 20; ! 1
Constant QP_HES      = 21;
Constant QP_IS       = 22; ! 1
Constant QP_HAS      = 23; ! 1
Constant QP_DOES     = 24; ! 1
Constant QP_S        = 25; ! 1
Constant QP_ES       = 26; ! 1
Constant QP_INORON   = 27; ! 1
Constant QP_OFFOROUTOF = 28;
Constant QP_INORONTHE= 29; ! 1
Constant QP_IF       = 30;
Constant QP_IFNOT    = 31;
Constant QP_IFSTRING = 32;
Constant QP_QUESTION = 33;
Constant QP_EMPH     = 34;
Constant QP_BOLD     = 35;
Constant QP_ROMAN    = 36;
Constant QP_THEIS    = 37;
Constant QP_HEIS     = 38;
Constant QP_WAS      = 39;
Constant QP_THEWAS   = 40;
Constant QP_THATWAS  = 41;
Constant QP_OFFTHE   = 42;
Constant QP_HEWAS    = 43;
Constant QP_IFSPACE  = 44;
Constant QP_SPACEIF  = 45;

[ QPrint txt p1 p2 p3 f l i  t m bfl;
   QPArg-->0 = p1;
   QPArg-->1 = p2;
   QPArg-->2 = p3;
   QPArg-->3 = f;
   QPArg-->4 = l;
   QPArg-->5 = i;
   t = MAX_QPRINT_LENGTH;
#ifdef TARGET_GLULX;
   l = PrintAnyToArray(QPchar,MAX_QPRINT_LENGTH,txt);
#ifnot; ! TARGET_ZCODE
   @storew QPchar 0 t;
   @output_stream 3 QPchar;
   if (txt ofclass String)
      print (string) txt;
   else if (txt ofclass Routine)
      txt.call();
   else txt();
   @output_stream -3;
   l = QPchar-->0; ! l == length of txt
#endif; ! TARGET_
   if (l>MAX_QPRINT_LENGTH)
   {
      print "^[Exceeded size for QPrint text on^ ~";
      switch (metaclass(txt))
      {
	String: print (string) txt;
	Routine: txt();
      }
      "~^ which is ",l," characters long.]^";
   }
#IfDef TARGET_GLULX;
   i = 0;
#IfNot; ! TARGET_ZCODE
   i = 2; l = l + 2;
#EndIf; ! TARGET_

!   print "[qprinting ~",(string)txt,"~^~";
!   for (: i<l: i++)
!   {
!      print (char) (QPchar->i);
!   }
!   if (WORDSIZE==2) i = 2; else i = 0;
!   print "~^]";

   for (: i < l: i++) if (QPchar->i==QP_esc_ch)
   {
      i++;
      if (QPchar->i==QP_esc_ch) { print (char) QP_esc_ch;  continue; }
!      print (char)'<';
      QPchar->QP_POS_INDEX = i; ! store pos at end of array
      bfl = 0;
      t = QPType();
      !print "[pos is ",QPchar->MAX_QPRINT_LENGTH," ('",
!	  (char) (QPchar->(QPchar->MAX_QPRINT_LENGTH)),"')]";
      f = (t&QP_CAPS);
      t = (t &~QP_CAPS);
      if (t==QP_IF) bfl = 1;
      if (t==QP_IFNOT) bfl = 2;
      if (t==QP_IFSPACE) bfl = 3;
      if (t==QP_SPACEIF) bfl = 4;
      if (bfl)
      {
	 i = QPParam();
	 (QPchar->QP_POS_INDEX)++;
	 t = QPType();
	 f = (t&QP_CAPS);
	 t = (t&~QP_CAPS);
	 switch (bfl)
	 {
	   1: if (i==0) bfl = 1; else bfl = 0;
	   2: if (i) bfl = 1;    else bfl = 0;
	   3: if (i==0) bfl = 1; else bfl = 2; !print with space
	   4: if (i==0) bfl = 1;
	      else { print (char)' '; bfl = 0; }
	 }
 	 !bfl==1 means "do not print"
      }
      if (t==0)
      {
	 i = QPchar->QP_POS_INDEX;
	 print "[ QPrint failed at index ",i,": '",
 	     (char) (QPchar->i),"']^";
	 while (i<l && QPchar->i~=QP_esc_ch) i++;
	 continue;
      }

      p1 = 0; p2 = 0; p3 = 0; i = 0;
      ! Set m to number of args required
      m = 1;
      if (t==QP_ROUTINE) m = 4;
      if (t==QP_EMPH or QP_BOLD or QP_ROMAN) m = 0;
      if (t==QP_NAME or QP_THE or QP_THEACC or QP_A or QP_GEN
	  or QP_THEIS or QP_THATACC or QP_THEWAS) m = 2;
      if (t==QP_IFSTRING) m = 3;
      
      if (m>0) p1 = QPParam(); !(1+QPchar->MAX_QPRINT_LENGTH);
!      print "[pos is ",QPchar->MAX_QPRINT_LENGTH," ('",
!	  (char) (QPchar->(QPchar->MAX_QPRINT_LENGTH)),"')]";
      if (m>1) p2 = QPParam(); !(1+QPchar->MAX_QPRINT_LENGTH);
      if (m>2) p3 = QPParam(); !(1+QPchar->MAX_QPRINT_LENGTH);
      if (m>3) i  = QPParam(); !(1+QPchar->MAX_QPRINT_LENGTH);
      
      if (p1==p2) switch (t)
      {
	QP_NAME, QP_THE: t = QP_HE;
	QP_THEACC, QP_A, QP_THATACC: t = QP_SELF;
	QP_GEN:          t = QP_HIS;
	QP_THEIS:        t = QP_HEIS;
	QP_THEWAS:       t = QP_HEWAS;
      }

      if (bfl~=1) QPrintType(t,p1,p2,p3,i,f);
      if (bfl==2) print (char)' ';

      i = QPchar->QP_POS_INDEX;

    }
    else print (char) (QPchar->i);
];

[ QPrintType t p1 p2 p3 p4 f sp;
   sp = 0;

#IfDef USE_PERSON1;
  #IfDef Person1;
   sp = Person1;
  #IfNot;
   sp = player;
  #EndIf;
   if (sp~=p1) sp = 0;
   if (sp && t==QP_A && f) t = QP_THE;
   if (sp) switch (t)
   {
     QP_NAME, QP_THE, QP_HE, QP_THAT: print (char) 'I';
     QP_THEACC, QP_A, QP_HIM, QP_THATACC:
        if (f) print "Me"; else print "me";
     QP_GEN, QP_HIS: if (f) print "My"; else print "my";
     QP_HERS: print "mine";
     QP_SELF: print "myself";
     QP_THATS, QP_HES: print "I'm";
     QP_THATSNOT: print "I'm not";
     QP_IS: print "am";
     QP_HAS: print "have";
     QP_DOES: print "do";
     QP_S,QP_ES:
     QP_THEIS, QP_HEIS: print "I am";
     QP_WAS: if (f) print "Was"; else print "was";
     QP_THEWAS, QP_THATWAS, QP_HEWAS: print "I was";
     default: sp = 0;
   }
   if (sp) return;
#EndIf;

#IfDef Person2;
   sp = person2;
#ifnot;
   sp = player;
#endif;
   if (sp && sp==p1) sp = true;
   else sp = false;

   if (sp) switch (t)
   {
     QP_NAME, QP_THE, QP_HE, QP_THAT, QP_THEACC, QP_A, QP_HIM, QP_THATACC:
      if (f) print "You"; else  print "you";
     QP_GEN, QP_HIS: if (f) print "Your"; else print "your";
     QP_HERS: print "mine";
     QP_SELF: print "yourself";
     QP_THATS, QP_HES: if (f) print "You're"; else print "you're";
     QP_THATSNOT: if (f) print "You're not"; else print "you're not";
     QP_IS: print "are";
     QP_HAS: print "have";
     QP_DOES: print "do";
     QP_S, QP_ES:
     QP_THEIS, QP_HEIS: if (f) print "You are"; else print "you are";
     QP_WAS: if (f) print "Were"; else print "were";
     QP_THEWAS, QP_THATWAS, QP_HEWAS:
      if (f) print "You were"; else print "you were";
     default: sp = false;
   }
   if (sp) return;

   switch (t)
   {
     QP_ROUTINE: p1.call(p2,p3,p4);
     QP_ADDRESS: print (address) p1;
     QP_STRING:  print (string) p1;
     QP_CHAR:    print (char) p1;
     QP_NUMBER:  print p1;
      
     QP_NAME: print (name) p1;
     QP_THE, QP_THEACC: if (f) print (The) p1; else print (the) p1;
     QP_A: if (f) print (A) p1; else print (a) p1;
     QP_GEN:
      if (f) print (The) p1;
      else print (the) p1;
      ! slightly dodgy, this
      print (char)''';
      if (p1 hasnt pluralname) print (char)'s';
     QP_HE:
      if (f)
      {  if (p1 has pluralname) print "They";
         else if (p1 has female) print "She";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "He";
	 else print "It";
      } else {
	 if (p1 has pluralname) print "they";
	 else if (p1 has female) print "she";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "he";
	 else print "it";
      }
     QP_HIM: if (p1 has pluralname) print "them";
	 else if (p1 has female) print "her";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "him";
	 else print "it";
     QP_HIS: if (f)
      {  if (p1 has pluralname) print "Their";
          else if (p1 has female) print "Her";
	  else if (p1 has male || p1 has animate && p1 hasnt neuter)
	     print "His";
	  else print "Its";
      } else {
	 if (p1 has pluralname) print "their";
	  else if (p1 has female) print "her";
	  else if (p1 has male || p1 has animate && p1 hasnt neuter)
	     print "his";
	  else print "its";
      }
     QP_HERS: if (p1 has pluralname) print "theirs";
	 else if (p1 has female) print "hers";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "his";
	 else print "its";
     QP_SELF: if (p1 has pluralname) print "themselves";
	 else if (p1 has female) print "herself";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "himself";
	 else print "itself";
     QP_THAT: if (f)
      {  if (p1 has pluralname) print "Those";
	 else if (p1 has female) print "She";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "He";
	 else  print "That";
      } else {
	 if (p1 has pluralname) print "those";
	 else if (p1 has female) print "she";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "he";
	 else  print "that";
      }
     QP_THATACC:
      if (p1 has pluralname) print "those";
      else if (p1 has female) print "her";
      else if (p1 has male || p1 has animate && p1 hasnt neuter)
	 print "him";
      else  print "that";
     QP_THATS: if (f)
      {  if (p1 has pluralname) print "Those are";
         else if (p1 has female) print "She's";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "He's";
	 else  print "That's";
      } else {
	 if (p1 has pluralname) print "those are";
	 else if (p1 has female) print "she's";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "he's";
	 else  print "that's";
      }
     QP_THATSNOT: if (f)
      {  if (p1 has pluralname) print "Those aren't";
	 else if (p1 has female) print "She's not";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "He's not";
	 else  print "That's not";
      } else {
	 if (p1 has pluralname) print "those aren't";
	 else if (p1 has female) print "she's not";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "he's not";
	 else  print "that's not";
      }
     QP_HES: if (f)
      {  if (p1 has pluralname) print "They're";
	 else if (p1 has female) print "She's";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "He's";
	 else  print "It's";
      } else {
	 if (p1 has pluralname) print "they're";
	 else if (p1 has female) print "she's";
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	    print "he's";
	 else  print "it's";
      }
     QP_IS: if (p1 has pluralname) print "are";
       else print "is";
     QP_HAS: if (p1 has pluralname) print "have";
       else print "has";
     QP_DOES: if (p1 has pluralname) print "do";
       else print "does";
     QP_S: if (p1 hasnt pluralname) print (char)'s';
     QP_ES: if (p1 hasnt pluralname) print "es";
     QP_INORON: if (p1 has supporter)
       { if (f) print "On"; else print "on"; }
       else if (f) print "In"; else print "in";
     QP_OFFOROUTOF: if (p1 has supporter) print "off";
       else print "out of";
     QP_INORONTHE: if (p1 has supporter)
       { if (f) print "On "; else print "on "; }
       else if (f) print "In "; else print "in ";
       print (the) p1;
     QP_IFSTRING: if (p1) { if (p2) print (string) p2; }
       else if (p3) print (string) p3;
     QP_QUESTION:
      QPoutnext1(p1);
      QPoutnext(p1);
     QP_EMPH: style underline;
      if (QPoutnext()) style roman;
     QP_BOLD: style bold;
      if (QPoutnext()) style roman;
     QP_ROMAN: style roman;
      QPoutnext();
     QP_THEIS: 
      if (f) print (The) p1; else print (the) p1;
      if (p1 has pluralname) print (string) ARE__TX;
      else print (string) IS__TX;
     QP_HEIS:
      if (p1 has pluralname)
      { if (f) print "They"; else print "they"; }
      else if (p1 has female)
      { if (f) print "She"; else print "she"; }
      else if (p1 has male || p1 has animate && p1 hasnt neuter)
      { if (f) print "He"; else print "he"; }
      else if (f) print "It"; else print "it";
      if (p1 has pluralname) print (string) ARE__TX;
      else print (string) IS__TX;
     QP_WAS:
      if (p1 has pluralname) print "were";
      else print "was";
     QP_THEWAS:
      if (f) print (The) p1; else print (the) p1;
      if (p1 has pluralname) print " were";
      else print " was";
     QP_THATWAS:
      if (p1 has pluralname)
      { if (f) print "Those were"; else print "those were"; }
      else
      { if (p1 has female)
        { if (f) print "She"; else print "she"; }
        else if (p1 has male || p1 has animate && p1 hasnt neuter)
        { if (f) print "He"; else print "he"; }
        else if (f) print "That"; else print "that";
	print " was";
      }
     QP_OFFTHE:
      if (p1 has supporter)
      { if (f) print "Off "; else print "off "; }
      else if (f) print "Out of "; else print "out of ";
      print (the) p1;
     QP_HEWAS:
      if (p1 has pluralname)
      { if (f) print "They were"; else print "they were"; }
      else
      {  if (p1 has female)
         { if (f) print "She"; else print "she"; }
	 else if (p1 has male || p1 has animate && p1 hasnt neuter)
	 { if (f) print "He"; else print "he"; }
	 else if (f) print "It"; else print "it";
	 print " was";
      }
   }
];

[ QPrintRet txt p1 p2 p3 p4 p5 p6;
   QPrint(txt,p1,p2,p3,p4,p5,p6);
   new_line;
   rtrue;
];
! alias for QPrintRet
[ QPR txt p1 p2 p3 p4 p5 p6;
   QPrint(txt,p1,p2,p3,p4,p5,p6);
   new_line;
   rtrue;
];

[ QPType pos tl l c fl;
   pos = QPchar->QP_POS_INDEX; ! pos = cur position
   if (QPchar->pos==QP_esc_ch) rfalse;
   tl = 2 + QPchar-->0; ! tl = total length
   l = pos; ! l starts at cur position
   while (l<tl && QPchar->l~=' ' or QP_esc_ch) l++;
   QPchar->QP_POS_INDEX = l;
   l = l - pos; ! l == length of tag to read
   if (l<1) rfalse;
   pos = pos + QPchar; ! pos == array starting at position
   c = pos->0;
   if (c>='A' && c<='Z')
   {
      fl = QP_CAPS;
      c = c+'a'-'A';
   }
   else fl = 0;
   if (l==1) ! t (the), s (-s), f (function), a (indef art),
      ! ? (quick if construct)
   {
      switch (c)
      {
	'a': return fl|QP_A;
	's': return fl|QP_S;
	'f': return fl|QP_ROUTINE;
	't': return fl|QP_THE;
	'?': return QP_QUESTION;
      }
      rfalse;
   }
   if (l==2) ! an (indef art), ch(ar), do, es (-es),
     ! he (nominative pronoun), is, in / on, It (nom pronoun),
     ! it (acc pronoun), if (if construct)
   {
      switch (c)
      {
	'a': if (pos->1=='n') return fl|QP_A;
	'c': if (pos->1=='h') return fl|QP_CHAR;
	'd': if (pos->1=='o') return fl|QP_DOES;
	'e': if (pos->1=='s') return fl|QP_ES;
	'h': if (pos->1=='e') return fl|QP_HE;
	'i': if (pos->1=='s') return fl|QP_IS;
	 if (pos->1=='n') return fl|QP_INORON;
	 if (pos->1=='t')
	 {
	    if (fl) return QP_CAPS|QP_HE;
	    return QP_HIM; ! note this
	 }
	 if (pos->1=='f') return fl|QP_IF;
	'o': if (pos->1=='n') return fl|QP_INORON;
      }
      rfalse;
   }
   if (l==3)
      ! are, gen(itive), has, him/her (acc pronoun),
      !  his (gen pronoun), hes (he's, she's, it's, they're),
      !  if~ (ifnot), ifs (ifstring), off, obj, num(ber), str(ing),
      !  she, the, was, tis (the is), if_ (if,space)
   {
      switch (c)
      {
	'a': if (pos->1=='r' && pos->2=='e') return fl|QP_IS;
	'g': if (pos->1=='e' && pos->2=='n') return fl|QP_GEN;
	'h': if (pos->1=='a' && pos->2=='s') return fl|QP_HAS;
	 if (pos->1=='i')
	 {
	    if (pos->2=='m') return fl|QP_HIM;
	    if (pos->2=='s') return fl|QP_HIS;
	 }
	 if (pos->1=='e')
	 {
	    if (pos->2=='r') return fl|QP_HIM;
	    if (pos->2=='s') return fl|QP_HES;
	 }
	'i': if (pos->1=='f')
	 {
	   if (pos->2==126 or 33) ! tilde or exclamation
	      return fl|QP_IFNOT;
	   if (pos->2=='s') return fl|QP_IFSTRING;
	   if (pos->2=='_') return fl|QP_IFSPACE;
	 }
	'_': if (pos->1=='i' && pos->2=='f') return QP_SPACEIF;
		 
	'o': if (pos->1=='f' && pos->2=='f') return fl|QP_OFFOROUTOF;
	 if (pos->1=='b' && pos->2=='j') return fl|QP_OBJECT;
	'n': if (pos->1=='u' && pos->2=='m') return fl|QP_NUMBER;
	's': if (pos->1=='t' && pos->2=='r') return fl|QP_STRING;
	 if (pos->1=='h' && pos->3=='e') return fl|QP_HE;
	't': if (pos->1=='h' && pos->2=='e') return fl|QP_THE;
	 if (pos->1=='i' && pos->2=='s') return fl|QP_THEIS;
        'w': if (pos->1=='a' && pos->2=='s') return fl|QP_WAS;
      }
      rfalse;
   }
   if (l==4) ! addr(ess) / word, bold, char, does, emph(asis), have,
             !  have, hers, heis / itis, name, rout(ine),
             !  self (itself &c.), shes, that, were, twas (the was)
             ! thac (that accusative)
   {
      switch (c)
      {
	'a': if (pos->1=='d' && pos->2=='d' && pos->3=='r')
	   return fl|QP_ADDRESS;
	'b': if (pos->1=='o' && pos->2=='l' && pos->3=='d')
	   return fl|QP_BOLD;
	'c': if (pos->1=='h' && pos->2=='a' && pos->3=='r')
	   return fl|QP_CHAR;
	'd': if (pos->1=='o' && pos->2=='e' && pos->3=='s')
	   return fl|QP_DOES;
	'e': if (pos->1=='m' && pos->2=='p' && pos->3=='h')
	   return fl|QP_EMPH;
	'h': if (pos->1=='a' && pos->2=='v' && pos->3=='e')
	   return fl|QP_HAS;
	 if (pos->1=='e' && pos->2=='r' && pos->3=='s')
	    return fl|QP_HERS;
	 if (pos->1=='e' && pos->2=='i' && pos->3=='s')
	    return fl|QP_HEIS;
	'i': if (pos->1=='t' && pos->2=='i' && pos->3=='s')
	    return fl|QP_HEIS;
	'n': if (pos->1=='a' && pos->2=='m' && pos->3=='e')
	   return fl|QP_NAME;
	'r': if (pos->1=='o' && pos->2=='u' && pos->3=='t')
	   return fl|QP_ROUTINE;
	's': if (pos->1=='e' && pos->2=='l' && pos->3=='f')
	   return fl|QP_SELF;
	 if (pos->1=='h' && pos->2=='e' && pos->3=='s')
	    return fl|QP_HES;
	't': if (pos->1=='h' && pos->2=='a' && pos->3=='t')
	   return fl|QP_THAT;
	 if (pos->1=='w' && pos->2=='a' && pos->3=='s')
	    return fl|QP_THEWAS;
	 if (pos->1=='h' && pos->2=='a' && pos->3=='c')
	   return fl|QP_THATACC;
	'w': if (pos->1=='o' && pos->2=='r' && pos->3=='d')
	   return fl|QP_ADDRESS;
          if (pos->1=='e' && pos->2=='r' && pos->3=='e')
           return fl|QP_WAS;
      }
      rfalse;
   }
   if (l==5) ! inthe / onthe, thats, ifnot (if-not contruct),
             ! yours, roman (style), theis, sheis, hewas
   {
      if (c=='i' or 'o' && pos->1=='n' && pos->2=='t'
 	  && pos->3=='h' && pos->4=='e')
	 return fl|QP_INORONTHE;
      if (c=='t' && pos->1=='h' && pos->2=='a'
 	  && pos->3=='t' && pos->4=='s')
	 return fl|QP_THATS;
      if (c=='i' && pos->1=='f' && pos->2=='n'
	  && pos->3=='o' && pos->4=='t')
	 return fl|QP_IFNOT;
      if (c=='y' && pos->1=='o' && pos->2=='u'
	  && pos->3=='r' && pos->4=='s')
	 return fl|QP_HERS;
      if (c=='r' && pos->1=='o' && pos->2=='m'
	  && pos->3=='a' && pos->4=='n')
	 return fl|QP_ROMAN;
      if (c=='t' && pos->1=='h' && pos->2=='e'
	  && pos->3=='i' && pos->4=='s')
	 return fl|QP_THEIS;
      if (c=='s' && pos->1=='h' && pos->2=='e'
	  && pos->3=='i' && pos->4=='s')
	 return fl|QP_HEIS;
      if (c=='h' && pos->1=='e' && pos->2=='w'
	  && pos->3=='a' && pos->4=='s')
	 return fl|QP_HEWAS;
      rfalse;
   }
   if (l==6) ! theacc (the accusative), thewas, string, number,
             ! object, offthe, hisort
   {
      if (c=='t' && pos->1=='h' && pos->2=='e'
	  && pos->3=='a' && pos->4=='c' && pos->5=='c')
	 return fl|QP_THEACC;
      if (c=='t' && pos->1=='h' && pos->2=='e'
          && pos->3=='w' && pos->4=='a' && pos->5=='s')
         return fl|QP_THEWAS;
      if (c=='s' && pos->1=='t' && pos->2=='r'
	  && pos->3=='i' && pos->4=='n' && pos->5=='g')
	 return fl|QP_STRING;
      if (c=='n' && pos->1=='u' && pos->2=='m'
	  && pos->3=='b' && pos->4=='e' && pos->5=='r')
	 return fl|QP_NUMBER;
      if (c=='o' && pos->1=='b' && pos->2=='j'
	  && pos->3=='e' && pos->4=='c' && pos->5=='t')
	 return fl|QP_OBJECT;
      if (c=='o' && pos->1=='f' && pos->2=='f' &&
	  pos->3=='t' && pos->4=='h' && pos->5=='e')
	 return fl|QP_OFFTHE;
      rfalse;
   }
   if (l==7) ! address, routine, thatacc (that accusative),
             ! thatwas
   {
      if (c=='a' && pos->1=='d' && pos->2=='d' && pos->3=='r'
	  && pos->4=='e' && pos->5=='s' && pos->6=='s')
	 return fl|QP_ADDRESS;
      if (c=='r' && pos->1=='o' && pos->2=='u' && pos->3=='t'
	  && pos->4=='i' && pos->5=='n' && pos->6=='e')
	 return fl|QP_ROUTINE;
      if (c=='t' && pos->1=='h' && pos->2=='a' && pos->3=='t'
	  && pos->4=='a' && pos->5=='c' && pos->6=='c')
	 return fl|QP_THATACC;
      if (c=='t' && pos->1=='h' && pos->2=='a' && pos->3=='t'
	  && pos->4=='w' && pos->5=='a' && pos->6=='s')
	 return fl|QP_THATWAS;
      rfalse;
   }
   if (l==8) ! thatsnot (that's not)
   {
      if (c=='t' && pos->1=='h' && pos->2=='a' && pos->3=='t'
 	  && pos->4=='s' && pos->5=='n' && pos->6=='o' && pos->7=='t')
	 return fl|QP_THATSNOT;
!      rfalse;
   }
   rfalse;
];

[ QPoutnext1 fl  pos tl;
   pos = QPchar->QP_POS_INDEX;
   tl = QPchar-->0;
#IfNDef TARGET_GLULX;
   tl = tl + 2; ! tl == total length
#EndIf;
   if (pos>=tl-1 || QPchar->pos==QP_sep_ch or QP_esc_ch) rfalse;
   pos++;

   while (pos<tl && QPchar->pos~=QP_sep_ch or QP_esc_ch)
   {
      if (fl) print (char) (QPchar->pos);
      pos++;
   }
   QPchar->QP_POS_INDEX = pos;
   rtrue;
];

[ QPoutnext fl  pos tl;
   fl = ~~fl;
   pos = QPchar->QP_POS_INDEX;
   tl = QPchar-->0;
#IfNDef TARGET_GLULX;
   tl = tl + 2; ! tl == total length
#EndIf;
   if (pos>=tl-1 || QPchar->pos==QP_esc_ch) rfalse;
   pos++;
   
   while (pos<tl && QPchar->pos~=QP_esc_ch)
   {
      if (fl) print (char) (QPchar->pos);
      pos++;
   }

   QPchar->QP_POS_INDEX = pos;
   rtrue;
];
	  
[ QPParam pos tl l;
   pos = QPchar->QP_POS_INDEX; ! pos == cur position
   tl = 2+QPchar-->0;
#IfNDef TARGET_GLULX;
   tl = tl + 2; ! tl == total length
#EndIf;
   if (pos>=tl-1 || QPchar->pos==QP_esc_ch) rfalse;
   pos++;
   l = pos; ! l starts at cur pos
   while (l<tl && QPchar->l~=' ' or QP_esc_ch) l++;
   QPchar->QP_POS_INDEX = l;
   l = l - pos;
   pos = pos + QPchar;

   ! 1 to 6 (qprint parameters), a(ctor), n(noun), s(econd), p(layer)
   if (l==1) switch (pos->0)
   {
     '1' to '6': return QPArg-->(pos->0 -'1');
     'a','A': return actor;
     'n','N': return noun;
     's','S': return second;
     'p','P': return player;
!     '0': rfalse; ! unnecessary
     default: rfalse;
   }
   ! true, noun, self
   if (l==4)
   {
      if (pos->0=='t' && pos->1=='r' && pos->2=='u' && pos->3=='e')
	 return true;
      if (pos->0=='n' && pos->1=='o' && pos->2=='u' && pos->3=='n')
	 return noun;
      if (pos->0=='s' && pos->1=='e' && pos->2=='l' && pos->3=='f')
	 return self;
      rfalse;
   }
   ! actor, false
   if (l==5)
   {
      if (pos->0=='a' && pos->1=='c' && pos->2=='t'
	  && pos->3=='o' && pos->4=='r')
	 return actor;
!      if (pos->0=='f' && pos->1=='a' && pos->2=='l'
!	  && pos->3=='s' && pos->4=='e')
!         return false; ! unnecessary
      rfalse;
   }
   ! second, player
   if (l==6)
   {
      if (pos->0=='s' && pos->1=='e' && pos->2=='c'
	  && pos->3=='o' && pos->4=='n' && pos->5=='d')
	 return second;
      if (pos->0=='p' && pos->1=='l' && pos->2=='a'
	  && pos->3=='y' && pos->4=='e' && pos->5=='r')
	 return player;
      !rfalse;
   }
   rfalse;
];

#endif; ! QPrint
