---------------------------------------------------------------------------
Pname.h, Version 1.1, 10 May 2001, copyright 2001, by Neil Cerutti
(cerutti@together.net)

This version of pname.h is recommended for use only with version 6/10
of the Inform Library.

This is the documentation for pname.h, a package which, by supplementing
the object name recognition code in the Inform Library, greatly reduces
the need to write parse_name routines.  It contains the following sections:

    INTRODUCTION
    INSTALLATION and USAGE
    Q&A
    OPERATORS
    PARSING
    TECHNICAL NOTES
    RESERVED WORDS
    DISTRIBUTION
    NO WARRANTY
    SPECIAL THANKS
    VERSION HISTORY

---------------------------------------------------------------------------
INTRODUCTION

If your game includes objects with similar names occuring in the same place
(for example, a Kitchen containing a JUICY ORANGE and some ORANGE JUICE),
you'll be familiar with the problem of disambiguation: EXAMINE JUICY and
EXAMINE JUICE describe respectively the fruit and the beverage, but
EXAMINE ORANGE prompts for a more precise specification of which object is
meant.  To avoid irritating the player -- who may consider it 'obvious'
that in this case the fruit is the intended target -- you are then forced
to replace these objects' name properties (with their simple lists of
dictionary words) by more complex parse_name properties; these comprise
customized routines which can be tricky to write and debug.

The pname.h package defines a new object property, pname (short for phrase
name), with a similar look and feel to the standard name property: both
contain a list of dictionary words.  However, in a pname property the order
of the words is significant, and special operators '.p' '.or' and '.x'
enable you to embed some intelligence into the list. In most cases where
the standard name property isn't enough, you can now just replace it with a
pname property, rather than write a parse_name property routine.  For
example, to solve the problem above, all you need is:

    Object  example_fruit "juicy orange"
      with  pname '.x' 'juicy' 'orange',
            ... ;

    Object  example_beverage "orange juice"
      with  pname '.x' 'orange' 'juice',
            ... ;

That is, a pname property is like a name property that has the enhanced
capability of defining 'phrases' -- sequences of words -- that increase an
object's likelihood of being selected when the player types matching words.
In doing so, it provides an alternative to parse_name which is quick,
flexible, and easy to use.

---------------------------------------------------------------------------
INSTALLATION and USAGE

To incorporate this package into your program, do three things:

1.  Add four lines near the head of the program (before you include
    Parser.h).
     
    Replace MakeMatch;
    Replace Identical;
    Replace NounDomain;
    Replace TryGivenObject;

2.  Include the pname.h header just after you include Parser.h.

    Include "Parser";
    Include "pname.h";

3.  Add pname properties to those objects which require phrase
    recognition.

Here's an example of an Inform program which uses this package:

    Constant Story "TEST STORY";
    Constant Headline "^This is only a test^^";

    Replace MakeMatch;      ! /---- add these lines before Parser.h
    Replace Identical;      ! |
    Replace NounDomain;     ! |
    Replace TryGivenObject; !<'

    Include "Parser";
    Include "pname.h";      ! <---- add this line after Parser.h
    Include "VerbLib";

    ...

    Object  shed "garden shed"
      with  description "The wooden shed...",
            pname '.x' 'wooden' '.x' 'garden' 'shed',
            ... ;

    Object  path "garden path"
      with  description "The stony path...",
            pname '.x' 'stony' '.x' 'garden' 'path',
            ... ;

    Object  garden "garden"
      with  description "The garden...",
            name 'garden',
            ... ;

    ...

A pname property defines one or more phrases that can refer to the
given object, using dictionary words and simple operators.  For instance,
consider the pname declaration for the garden shed:

            pname '.x' 'wooden' '.x' 'garden' 'shed'

The "garden shed" object's pname property contains one phrase in which
the words 'wooden' and 'garden' are optional and 'shed' is mandatory.
During parsing, the property generates a 'phrase match' -- a signal which
increases its chance of being picked without the need for a disambiguation
prompt -- in response to the inputs: WOODEN GARDEN SHED, WOODEN SHED,
GARDEN SHED, and SHED. (For that matter, it would also generate matches for
GARDEN GARDEN WOODEN WOODEN WOODEN GARDEN SHED, but not for
TIMBER SHED or SHED WOODEN GARDEN.)

---------------------------------------------------------------------------
Q&A

Q:  In what ways is the pname property different from the name property?

A:  In a pname property, the order in which the words appear makes a
    difference during the disambiguation process.  In the standard name
    property, word order doesn't matter.

    In addition, you can include operators in your pname properties to
    help define them more precisely. The name property is always
    completely general.


Q:  Can't I already do this sort of thing with a parse_name routine?

A:  Not exactly, not as easily, not as generally, and not as quickly.
    (Also, don't worry if you don't understand parse_name routines... you
    won't have much need of them if you use pname.h.)

    For example, suppose your game contains some coffee and a coffee table:
    
        Object  coffee "coffee"
          with  name 'coffee',
                description "It's piping hot, with a stern warning label
                written on the side concerning debilitating tongue burns.",
                ... ;

        Object  coffee_table "coffee table"
          with  name 'coffee' 'table',
                description "It's rectangular, made of black marble 
                and has an overstuffed magazine-rack underneath.",
                ... ;

    However, disambiguation fails if the coffee and the coffee table are
    ever in scope together:

        You can see a coffe table and some coffee here.
        
        >EXAMINE COFFEE
        Which do you mean, the coffee or the coffee table?

        >COFFEE
        Which do you mean, the coffee or the coffee table?

    The coffee has become impossible to refer to!  The traditional solution
    to this problem is to put a restrictive parse_name routine on the
    coffee table, which forces the game never to recognize the word COFFEE
    alone as referring to the table.

        Object  coffee_table "coffee table"
          with  parse_name [ wd;
                    wd = NextWord();
                    if (wd == 'coffee' && NextWord() == 'table') 
                      return 2;
                    else if (wd == 'table') 
                      return 1;
                    else 
                      return 0;
                    ],
                description "It's rectangular... ",
                ... ;

    This solution works great when both objects are in scope:

        You can see a coffee table and some coffee here.
        
        >EXAMINE COFFEE
        It's piping hot...

        >EXAMINE COFFEE TABLE
        It's rectangular...

    But there's still a trap. This doesn't work when the coffee table is in
    scope but the coffee is not, causing frustration to players when
    perfectly unambiguous input is rejected:

        You can see a coffee table here.

        >EXAMINE COFFEE
        You can't see any such thing.

    In this case, the parse_name routine is too restrictive.  A more
    flexible routine would know that, when the coffee is not in scope, it's
    okay for COFFEE alone to refer to the coffee table.  You can fix this 
    problem by enhancing your parse_name, but you'll affect performance:

        Object  coffee_table "coffee table"
          with  name 'coffee' 'table',
                parse_name [ wd;
                    if (~~TestScope(coffee)) 
                      return -1; ! use name prop instead
                    wd = NextWord();
                    if (wd == 'coffee' && NextWord() == 'table') 
                      return 2;
                    else if (wd == 'table') 
                      return 1;
                    else 
                      return 0;
                    ],
                description "It's rectangular... ",
                ... ;

    Here, the table's parse_name routine applies only when the coffee isn't
    in scope. However, calling TestScope() is computationally expensive;
    calling it from within a parse_name routine is very expensive, and
    doing so will noticeably slow down your game even on a fast computer.

    The situation, already messy, becomes worse if another ambiguous object
    (for example, a coffee table book) is added to the game; you're then
    forced to modify all your working parse_name routines.

    But, help is at hand!  Using pname.h, you can simply code:

        Object  coffee "coffee"
          with  name 'coffee',
                description "It's piping hot... ",
                ... ;

        Object  coffee_table "coffee table"
          with  pname '.x' 'coffee' 'table',
                description "It's rectangular... ",
                ... ;

        Object  coffee_table_book "coffee table book"
          with  pname '.x' 'coffee' '.x' 'table' 'book',
                description "Hard covered and beautifully decorated,... ",
                ... ;


Q:  Cool!  So I should use pname properties on all my objects, right?

A:  That's not necessary. You should use pname properties only when 
    disambiguation might become a problem, such as the coffee table
    situation described above.  There is no advantage to using them when
    Inform's normal system of name recognition is sufficient.  For example,
    if you have a coffee mug and a litter bug in your game, the name
    property is capable of distinguishing between them and using a pname
    property would be a waste.


Q:  I defined a pname property for the black rod from Adventure:

        Object  black_rod "black rod"
          with  pname 'black' 'rod',
                ... ;

    Since both words 'black' and 'rod', are mandatory in that phrase, the
    single word BLACK and the phrase ROD BLACK will never refer to my
    object.  Is that right?

A:  It depends; the object could still end up being the most likely match
    in scope. Remember that the pname property acts only as an _aid_ to
    disambiguation.  If the player's input isn't ambiguous then the parser
    selects the black rod even if the phrase 'black' 'rod' isn't matched
    by the input.  The phrase matcher in pname.h is not restrictive;
    rather, it applies bonuses during the disambiguation process for
    phrases that are matched.


Q:  Oh. So what's a phrase, and what's a phrase match?

A:  A phrase is a string of dictionary words in a pname property, possibly
    containing operators.  More than one phrase can appear in a single
    pname property if they are separated by the '.p' phrase separator
    operator.

    A phrase match results when the words input by the player match one of
    the phrases specified in a pname property.  If there is a phrase match,
    the object receives a bonus during disambiguation.


Q:  Is pname.h Glulx compatible? In other words, Can I use pname.h in an
    Inform program designed to be compiled for the new Glulx virtual
    machine?

A:  Yes.


Q:  OK. I'm convinced that pname.h will make my life easier. But what are 
    the disadvantages of using pname.h in my program, if any?

A:  The biggest disadvantage is that pname.h replaces several important
    routines that are integral to the Inform Library.  You are effectively
    locked into using a specific Library version with pname.h, and any
    modifications you have made to those routines in your copy of the
    Library will be overridden by pname.h.  This version of pname.h is
    designed for the Library version shown at the head of this file, and
    is very likely to be incompatible with past and future versions.

    A smaller disadvantage is that pname.h doesn't quite live up to its
    promise of obviating parse_name routines.  You will probably still need
    to write a few of them; see the TECHNICAL NOTES section below.

    An even smaller disadvantage is that it uses up one of Inform's limited
    stock of object attributes.

---------------------------------------------------------------------------
OPERATORS

The pname property takes a list of dictionary words in single quotes, in
exactly the same manner as name. The primary difference is that some of
those words can be the pname operators '.p', '.or', and '.x' which are
defined here.

'.p' phrase separator operator

    Used to separate phrase declarations in a pname property.  Everything
    after it and up to the end of the property or the next phrase
    separator is interpreted as a single phrase.

    All pname properties begin with an implied '.p' if no phrase
    separator is provided.  Using this operator, you can declare more than
    one phrase for the same object.  For example:

        pname 'old' 'brass' 'lantern' '.p' 'shining' 'light'

    defines two phrases: 'old' 'brass' 'lantern' and 'shining' 'light'.  It
    would generate phrase matches for the inputs OLD BRASS LANTERN and
    SHINING LIGHT, but not for BRASS LIGHT or SHINING LANTERN.

'.or' or operator

    A binary operator used in phrases to show that certain words in
    the phrase might be substituted for other words. For example:

        pname 'soda' '.or' 'pop' 'machine'

    would generate phrase matches for the inputs SODA MACHINE and POP
    MACHINE.  You can string together as many words as you like with this
    operator. For example:

        pname 'soda' '.or' 'pop' '.or' 'bubbly' '.or' 'coke' 'machine'

    A phrase match would then result from all of the following inputs:
    SODA MACHINE, POP MACHINE, BUBBLY MACHINE and COKE MACHINE.
    But there would be no phrase match for any of:
    MACHINE, SODA SODA MACHINE, SODA POP MACHINE, SODA BUBBLY MACHINE,
    MACHINE SODA or COKE.

'.x' optional operator

    A unary prefix operator, which introduces a dictionary word that is
    optional in a phrase. The first dictionary word to the right of a '.x'
    operator is interpreted as optional.  For example:

            pname 'soda' '.x' 'machine' 'button'

    denotes that the word 'machine' is optional in the above phrase. It
    would generate a phrase match for the inputs SODA MACHINE BUTTON and
    SODA BUTTON. There would be now match with MACHINE BUTTON, because
    'soda' is not marked as optional.

    You can string together as many optional words as you like; all input
    words that match any of the words in the string of options will be
    matched, in any order. For example:

            pname '.x' 'black' '.x' 'swimming' 'trunks'
    
    This would generate a phrase match for all of the following inputs:
    TRUNKS, BLACK TRUNKS, BLACK SWIMMING TRUNKS, SWIMMING TRUNKS,
    SWIMMING BLACK TRUNKS and SWIMMING SWIMMING BLACK BLACK SWIMMING TRUNKS.
    No phrase match results from any of: BLACK, SWIMMING or TREE TRUNKS.

    A pname property that contained all optional words would act just like
    a standard name property.

---------------------------------------------------------------------------
PARSING

The pname.h parser counts the number of words in the input that appear in
the pname property of the given object.  It also records whether or not the
input happens to match any of the phrases specified for the object.  If the
number of total words that match is equal to the longest possible phrase
match, then the object is marked as having matched a phrase in the input.
An object with a name property is treated as if it were a pname property in
which all the words are optional -- so every match results in a phrase
match.  If an object has both a name property and a pname property, pname
effectively masks the existence of the name property from the parser. The
same is true for parse_name routines. If you have a parse_name routine on
an object with a pname property, the parse_name will not be called during
parsing.

Next, during an early phase of Inform's disambiguation process, from all
the objects that match at least one word in the input, only the ones that
match the most consecutive words in the input are retained.

Finally, among the objects that matched the most words, only the ones that
generated a phrase match are retained (unless none of them generated a
phrase match, in which case they are all retained).

Thus, it's possible for an object that generates no phrase matches to be
selected as the most likely.  In the coffee table example in the Q&A section
above, when the coffee was not in scope and the player specified only
COFFEE, even though the single word COFFEE does not produce a phrase
match for the coffee table, the coffee table is still deemed the most
likely object by the parser because it matched a word in the input.

Here's a longer example and a more detailed explanation.

The starting location of _Uncle Zebulon's Will_ by Magnus Olsson has some
troubling parsing problems.  There's a garden, a garden path, and a garden
shed all in scope at once.  Using pname phrases, you can solve the problem
by making 'garden' optional for the garden path and the garden shed:

    Object  garden "garden"
      with  name 'garden',
            description "The garden...",
            ... ;

    Object  path "garden path"
      with  pname '.x' 'garden' 'path',
            description "The path...",
            ... ;

    Object  shed "garden shed"
      with  pname '.x' 'garden' 'shed',
            description "The shed...",
            ... ;

The following table shows phrase matches and word matches for several
possible inputs and explains which object results.

                    Words   
Input       Object  Matched Phrase match?
----------- ------- ------- ------------------------
GARDEN
            garden: 1       yes
            path:   1       no
            shed:   1       no
            -------------------
            result: garden


SHED
            garden: 0       no
            path:   0       no
            shed:   1       no
            -------------------
            result: shed


GARDEN SHED
            garden: 1       yes
            path:   0       no
            shed:   2       yes
            -------------------
            result: shed [it matched the most words - on that basis, it
                          would have won even if it had not generated a
                          phrase match]

PATH
            garden: 0       no
            path:   1       yes
            shed:   0       no
            -------------------
            result: path


GARDEN PATH
            garden: 1       yes
            path:   2       yes
            shed:   1       no
            -------------------
            result: path

---------------------------------------------------------------------------
TECHNICAL NOTES

I recommend you use only the latest version of the Inform Library.
You may use either the standard version by Graham Nelson or Andrew
Plotkin's cross-platform port for compiling Glulx files.

The pname parser considers as indistinguishable any two objects that have
equivalent name properties and duplicate pname properties. To be
indistinguishable, the name property of the two objects must be equivalent:
every word in one must appear somewhere in the other (See Nelson, Graham:
_The Inform Designer's Manual_, 3rd Ed, Section 25). pname properties
must be identical.

The pname property is additive. That means that pname properties inherit
the pname property of their parent class. The way this works in practice
is: the pname properties of the instance and its parent classes are
concatenated into a list and that list is assigned to the instance as its
pname property. When a name property is created through inheritance, the
order in which the properties are concatenated doesn't matter. However,
since the order of the words in a pname property is important, you need to
understand the order of inheritance.

Inheritance start with the instance and works its way back up through its
parent classes.

     Class Rod with pname 'rod' '.or' 'wand';
     
     Rod black_rod with pname '.x' 'black;

     Rod blue_rod with pname '.x' 'blue';

After compilation, the black rod's pname property is 

     '.x' 'black' 'rod' '.or' 'wand'
     
and the blue rod's is 

     '.x' 'blue' 'rod' '.or' 'wand'.

For multiple inheritance, the order in which the list is created is
equivalent to the order the classes are declared in the class clause of the
inheriting object.

    Class Rod with pname 'rod';

    Class Wand with pname 'wand';

    Object black_rod
     class Rod Wand
     with pname '.x' 'black';

In this case the pname property of the black_rod is 

     '.x' 'black' 'rod' 'wand'
     
which is probably not what you wanted! You have to be careful. 

If you don't want pname properties of Class objects to have a chance of
interfering with pname properties of its intances, you can include a phrase
separator operator in the declaration.

     Class Rod with pname '.p' 'rod';

     Class Wand with pname '.p' 'wand';

     Object black_rod
      class Rod Wand
      with pname 'black';

The black_rod, after compilation, will have a pname property of 'black'
'.p' 'rod' '.p' 'wand', in other words, a pname property which contains
three one-word phrases.

If a class contains a name property then it will be masked by the existence
of any instances that contain a pname property. pname_verify will print
warnings in this case (see DEBUGGING).

You may use plural flagged dictionary words in your pname properties, and
they will be recognized by the pname parser.  A plural dictionary word is
defined with a //p on the end (for example, 'dogs//p').  If the parser sees
one, it knows that the player may be referring to more than one object.
(See: Nelson, Section 25.)

There are some uses for parse_name routines that pname.h doesn't cover. A
parse_name must be used to dynamically change the name of an object during
the course of a game.  In addition, a parse_name routine may be needed by a
class of similar objects in order to be explicit about what is
indistinguishable and what isn't. As noted in the PARSING section above, do
not try to have a parse_name and a pname in the same object. They do not
coexist happily.

---------------------------------------------------------------------------
DEBUGGING

This package contains a useful set of debugging diagnostic statements for
times when thorny parsing issues arise (they were also of great help while
debugging this package!); these are compiled only if DEBUG is defined.  The
parser trace level must be set to 7 or higher for full phrase parsing
diagnostics to print (See: Nelson, Section 30), although trace level 5 is
enough to show which objects produced phrase matches.

Due to speed and algorithmic simplicity concerns, pname.h does not do error
checking for bad pname declarations during parsing. To ameliorate this
shortcoming, pname.h provides a pname_verify routine.  When DEBUG is
defined, you may call pname_verify() in your Initialise() routine to verify
the pname properties in your objects.  Here's what I suggest:

    [ Initialise;
        #ifdef DEBUG;
        pname_verify();
        #endif;
        ! etc...
    ];

pname_verify loops over all the game objects and reports errors and
warnings for pname properties it finds suspect. The game is aborted if any
errors are reported.

pname_verify report a warning if it finds 'x', 'p', or 'or' in a
pname property, since they are most likely typos for '.x', '.p' and '.or',
respectively.

pname_verify also reports a warning for objects with pname property and a
name or parse_name property, because a pname property masks both the
parse_name and name properties from the parser.

You can call pname_verify with 'true' as its argument to suppress these
warning messages:

    pname_verify(true); ! suppress warnings

---------------------------------------------------------------------------
RESERVED WORDS

In addition to the identifiers reserved by Inform and the standard Library,
pname.h defines the following.  You mustn't use any of the following words
in a program that Includes pname.h, except as documented here.

Globals:            matches_in_match_list

Constants:          PHRASE_OP           '.p'
                    OR_OP               '.or'
                    OPT_OP              '.x'

                    You may use any of these constants in your program
                    but you must not try to define them yourself.


Routines:           _pn_pname
                    _pn_matchPhrase
                    _pn_inpWord
                    _pn_inpLen
                    _pn_TryGivenObject

    additionally, if DEBUG is defined:     

                    pname_verify
                    _pn_printInpWord
                    _pn_printPhrase
                    _pn_isOp
                    _pn_error
                    _pn_warning

                    You shouldn't call any of the above routines in your
                    program except for pname_verify.

Attributes:         phrase_matched

                    This is used internally by the pname parser to mark
                    objects for which a phrase match has been detected.

Standard Library routines:
                    
                    MakeMatch
                    Identical
                    NounDomain
                    TryGivenObject

                    These Library routines are replaced in pname.h so
                    don't try to replace them yourself. If you want to hack
                    one of these routine in a program that includes pname.h
                    you will need to hack pname.h instead.

---------------------------------------------------------------------------
DISTRIBUTION

You may use the header file pname.h in any Inform program.  You may
distribute source code which uses pname.h, but you may not distribute
pname.h with your source code except as described below.

The header file pname.h itself may only be distributed if this
documentation file, pname.txt, is included and pname.h is not modified in
any way.  This documentation file, 'pname.txt' may not be altered without
the permission of the author, Neil Cerutti <cerutti@together.net>, except
if the contents remain unchanged.  It is permissible to adapt it to a
different file format, e.g., HTML, PDF, PS, however and I don't consider
such adaptation to be an alteration.

You may distribute compiled game files which include pname.h.  You may also
distribute compiled game files which include an altered version of pname.h.

---------------------------------------------------------------------------
NO WARRANTY

Because this software is distributed without charge, you assume the
entire responsibility for determining whether the program fits your
needs and whether it is correct and/or complete.

Neil Cerutti, and any distributors of this software specifically disclaim
any and all warranties, expressed or implied, including but not limited to
implied warranties of merchantability and fitness for a particular purpose,
with regard to this software. In no event will the author or any
distributor of this software be liable to you for any damages, including
lost profits, lost savings, or other incidental or consequential damages
arising out of the use or inability to use this software, even if any of
these parties has been advised of the possibility of such damages, or for
any claim by any other party.

---------------------------------------------------------------------------
SPECIAL THANKS 

Thanks to Roger Firth who submitted an unsolicited re-organization of this
file that helped transform it from a specification into documentation. His
rewrites were invaluable in writing this documentation.

Thanks to Marnie Parker and David Cornelson, who both looked at very early
drafts of this document and gave good advice and asked good questions, which
helped streamline and clarify the design of pname.h, which was originally
much more complex.

Thanks to Andrew Plotkin for donating the code for the cross-platform (Z
and Glulx) versions of Identical, TryGivenObject, MakeMatch and ParseNoun
from his cross-platform port of Inform Library 6/10.

---------------------------------------------------------------------------
VERSION HISTORY

1.0  18 April 2001
  Initial release

1.1  10 May 2001
  o  Fixed bug that caused to the parser to never generate a phrase match
	for a phrase in which an optional word was followed by the same
	consecutive word, or in an '.or' clause, e.g., '.x' 'card' 'card'.

	The phrase '.x' 'card' 'card' will now generate a 1 word phrase match
	for the input CARD and an N word phrase match for input containing N
	instances of the word CARD, i.e., CARD CARD CARD CARD would cause a 4
	word phrase match.
