





                       The Archetype Language Reference Manual

                                         by
                                   Derek T. Jones


          Overview

          An Archetype program consists of a series of object declarations.
          These declarations can be simplified by defining "types" so that
          the common attributes of several slightly different objects can
          be described once; this is known as "inheritance" in object-
          oriented parlance.

          Objects consist of changeable attributes, which describe an
          object's properties, and methods, which describe the object's
          behavior and how it depends on its own attributes and its
          interaction with other objects.  Methods are tied to messages;
          when the object receives that message, the method associated with
          it is invoked.

          Methods consist of one or more statements tied to a message.  To
          "invoke" a method means to execute its statements in order from
          top to bottom.  Some statements have a value; the value of the
          last statement to be executed is returned to the sender of the
          message that invoked the method.

          Archetype programs are executed by the PERFORM program after
          being translated into an intermediate binary form by the CREATE
          program.  The PERFORM program contains a single object named
          "system" which starts your program by sending the message 'START'
          to an object named "main".  For this reason every Archetype
          program has to have one (and only one) object named "main" which
          contains a method for the 'START' message.

          Object Declarations

          An object declaration has the following form:

          <type-name> ( <object-name> | null )

          ( <attribute-name> : <initial-value> )*

          [ methods

          ( <message> : <statement> )* ]

          end

          A typical object declaration:

          room bare_cell

            desc : "bleak, bare cell"













          methods

            'look'  : write "I'm standing in a ", desc, "."
            'north' : hallway

          end


          In the above example, the type "room" had already been defined.
          A type must be defined before it is used by an object.  The only
          type that is already defined for you is the "null" type.  The
          "null" type means that the only attributes and methods that the
          object contains are what appears in its particular declaration.
          Whenever an object is declared to be of a certain type, that
          object inherits the attributes and methods declared in the
          definition of the type.

          Please NOTE that when an attribute has a default expression, as
          above, the attribute contains the unevaluated form of the
          expression.  This is in contrast to what happens when the
          attribute is assigned a new value at run-time with the :=
          operator (see Operators and Expressions below).  What this means
          is that in an object declaration such as:

          null abc
            a   : 3
            def : a + 5
          end

          the value of the attribute "def" will change if the value of
          attribute "a" changes.

          When an object inherits an attribute or method, it means that
          unless that object specifically redefines that attribute or
          method, it will have the attribute or method defined by the type.
          Look at the example below:

          type shouter based on null

            name : "an unremarkable shouting object"

          methods

            'shout' : >>Shout, Shout, Ready Or Not!
            'who are you' :
              write "I am ", name, "."

          end

          shouter Bob

            name : "Bob"













          end


          The object Bob did not declare the method 'shout' or the method
          'who are you', but inherited them from the "shouter" declaration
          because it is a "shouter" type.  It did not inherit the "name"
          attribute because it had its own specific declaration for that.
          The code above could be replaced by the single declaration below:

          null Bob

            name : "Bob"

          methods

            'shout' : >>Shout, Shout, Ready Or Not!
            'who are you' :
              write "I am ", name, "."

          end


          For a single object, the code above seems smaller and simpler,
          and perhaps it is.  But if you had even two "shouter" objects,
          using a common type definition saves you time, makes the code
          clearer, and actually uses less memory while being executed.

          Type definitions can inherit attributes and methods from other
          type definitions.  In the above example, type "shouter" was based
          on "null", meaning that there was no inheritance.  But we could
          define a type "screamer" based on "shouter":

          type screamer based on shouter

            IsAscreamer : TRUE

          methods

            'scream' : >>I CAN SCREAM TOO!

          end

          Now if we make an object of that type:

          screamer Joe

            name : "Joe"

          methods

            'sing' : >>Tra la la!

          end













          it is the same as if we had declared Joe as:

          null Joe

            IsAscreamer : TRUE
            name : "Joe"

          methods

            'sing' : >>Tra la la!
            'scream' : >>I CAN SCREAM TOO!
            'shout' : >>Shout, Shout, Ready Or Not!
            'who are you' :
              write "I am ", name, "."

          end

          The object "Joe" inherits attributes and methods first from the
          "screamer" type, and then, because "screamer" is based on
          "shouter", from the "shouter" type.

          The trick of the "IsAscreamer" attribute is a common one in
          Archetype.  If every type declares an attribute named
          IsA<type-name>, it is easy to tell whether an object's particular
          "family tree" has that type in it:

          if any_object.IsAshouter then
            write any_object.desc, " is a shouter."

          Statements

          An Archetype method is a single statement.  However, this
          statement can be compound, that is, made up of several statements
          inside curly braces { } .  For example:

          null obj

          methods

            'ex1' : write "This is a single statement."
            'ex2' : {
              write "This is still a single statement because"
              write "both these statements are inside curly braces."
              }

            'ex3' :
              write "Although this might look like a compound statement,"
              write "it is not; this line and the next will be flagged"
              write "as errors by the CREATE program."

          end















          The 'ex3' method is only tied to the first statement.  The next
          two statements are "stranded" and will prevent the program from
          being compiled into an executable form.

          Reference To Archtype Statements

          How To Read This Manual:  Each statement is preceded by a single
          line followed by its complete syntax in Backus-Naur form.  If you
          are not familiar with Backus-Naur form, read Appendix A.
          include "filename"

          This is probably the first Archetype statement you will use, in
          order to include STANDARD.ACH.  The filename must be a string
          literal enclosed in double quotes.  You can put a pathname on the
          include file if you like.  The compiler will first search the
          current directory for the file and then the directory in which
          the compiler is located.  All of the text in the file is compiled
          as though you had included it there with your editor.

          ( write | writes | stop ) [ <expression> (, <expression> )* ]

          The write, writes, and stop statements all take zero or more
          comma-separated expressions and write them to the screen.  The
          write statement finishes off the line after writing its
          expressions.  The writes statement keeps the line "dangling" so
          that subsequent output appears on the same line.  The stop
          statement halts the Archetype program after writing its output
          and returns to DOS.

          All Archetype output is word-wrapped and "paged".  No words will
          be split across the right margin, and text will not flow off the
          top of the screen before the user can read it all.  After 23
          lines of text, a "Hit any key to continue" message is displayed
          and the rest of the text is shown after the key is hit.  In other
          words, all text output is "civilized" without the programmer
          having to program specifically for it.

          Examples:

          drinkable poison
            desc   : "golden goblet"
            filled : TRUE
          methods
            'look' : {
              writes "I look inside the ", desc, " and see "
              if filled then
                write "A frothing, noxious potion!"
              else
                write "nothing."
              }
            'drink' :
              if filled then
                stop "I drink it down... Garg!  I die in agony."
              else












                write "The ", desc, " is empty."
          end

          object manual
            desc     : "programmer's manual"
            location : desk
          methods
            'look' : write "As I look at the manual, I see that it has ",
                     "a useful tip on the use of a single write ",
                     "statement to make a block of free-flowing text ",
                     "that is guaranteed to wrap correctly:  using a ",
                     "series of comma-separated strings."
          end


          Returns:  The write statements always return the value of the
                    last <expression> they write.

          if <expression> then <statement> [ else <statement> ]

          The if statement is extremely important because it is one of the
          only ways of modifying an object's behavior based on any of its
          attributes.  <expression> is evaluated; if it is UNDEFINED,
          ABSENT, or FALSE, the statement following else is executed;
          otherwise the statment following then is executed.  If
          <expression> is UNDEFINED, ABSENT, or FALSE and there is no else
          branch in the if statement, nothing happens.  There must always
          be a then branch.  The <statement> following then or else can be
          another if statement.  One thing to be careful of when having
          another if statement follow a then is that the next else
          encountered will be considered a part of the most recent then.
          If this is not what you want, use curly braces to surround the
          inner if statement.

          Examples:

          object airlock

            open      : TRUE
            locked    : TRUE

          methods

            'leave' :
              if not spacesuit.wearing then
                stop "I'm not wearing a spacesuit!  I die."
              else if not spacesuit.patched then
                stop "All the air escapes through the hole in the suit!"
              else if not helmet.wearing then
                stop "I'm not wearing a helmet!  I die."
              else {
                write "I step out of the airlock."
                message --> object
                }













          end

          Returns:  The if expression returns the value of the last
                    executed <statement>.  If there is no else branch, and
                    <expression> is UNDEFINED, ABSENT, or FALSE, then the
                    value of the if statement is UNDEFINED.


          case <test expression> of {
               ( <expression> : <statement> )* [ default : <statement> ] }

          The case statement is a way of checking an expression to see if
          it matches one of several values.  <test expression> is evaluated
          only once.  Then the <expression> : <statement> pairs following
          the word of and a curly brace are evaluated from first to last
          until one of the <expression>s matches <test expression>.  If
          this happens, the <statement> following the colon is executed.
          The word default matches any <test expression> and therefore must
          be at the end of the case statement.  The case statment usually
          replaces a more complicated if-then-else-if... structure.

          Example:

          Instead of

          { tries +:= 1
            if tries = 1 then
              write "I try to open it with my bare hands, but I can't."
            else if tries = 2 then
              write "I try and try but it stays closed."
            else if tries = 3 then
              write "I scream with frustration, but the can won't open."
            else if tries = 4 then
              write "I pound the can upon the pavement.  Stays shut."
            else
              write "No.  I've given up." }

          you can write

            case tries +:= 1 of {
              1 : write "I try to open it with my bare hands, but I can't."
              2 : write "I try and try but it stays closed."
              3 : write "I scream with frustration, but the can ",
                              "won't open."
              4 : write "I pound the can upon the pavement.  Stays shut."
              default : write "No.  I've given up."
              }


          while <expression> do <statement>

          The while statement is the most straightforward way to do
          something repeatedly.  <expression> is evaluated; if it is












          UNDEFINED, ABSENT, or FALSE, the while statment ends.  If not,
          <statement> is executed and <expression> is evaluated again.

          There are two ways to stop a while loop:  <statement> must
          eventually cause <expression> to evaluate to UNDEFINED, ABSENT,
          or FALSE, or <statement> must contain the break statment.

          Note that if <expression> is UNDEFINED, ABSENT, or FALSE before
          entering the while loop, <statement> will never be evaluated.

          Examples:

          {         # sums up the numbers between 1 and x
            while x > 0 do {
              sum +:= x
              x -:= 1
              }
            write "The sum is ", sum
          }

          # interactive questioning

            while TRUE do {
              writes "What message would you like to send? "
              if (m := read) == "quit" then
                break
              else
                m -> obj
            }

          Note that in the above example, the only way the loop can exit is
          with the break statement, since <expression> will always be TRUE.

          for <expression> do <statement>

          The for statement is the other way of doing things repeatedly.
          It is used when you want to do some action to all or some set of
          the objects in your program.

          <expression> should contain the keyword each.  This keyword is
          set, in turn, to each object in your program.  Every time
          <expression> does not evaluate to UNDEFINED, ABSENT, or FALSE,
          <statement> is executed.  <statement> will usually contain the
          keyword each as well, which retains its value until <expression>
          is evaluated again.

          Examples:

          object purse

















          methods
            'empty' : {
              write "I empty out the purse and find:"
              for each.location = purse do {
                write each.desc
                each.location = player.location; 'MOVE' -> each
              }
            }
          end

          If you want <statement> to apply to every single object in your
          program, not just those that satisfy some <expression>, write

          for each do ...

          Note that even if <expression> only specifies a few objects out
          of the hundred or so in your program, all of the program's
          objects must be tested.  For this reason the for statement can
          take longer that it seems it should, especially for a large
          program.

          If you want to find just the first instance of the set of objects
          you're looking for, you can use the break statement to jump out
          the instant <statement> is evaluated:

          # player needs a container in their possession
          {
            useful := UNDEFINED
            for each.IsAcontainer and each.location = player do {
              useful := each
              break
            }
            if useful then
              write "You can use ", useful.desc, "."
            else
              write "You don't have anything useful."
          }

          create <type name> named <attribute>

          Most of the objects in your program will probably be defined
          within the program themselves.  However, there are some
          situations where you would like to add an object to your program
          dynamically, that is, after the program is already underway.  The
          create statement is the way to do this.  This statement
          instantiates an object of <type name> and causes <attribute> to
          point to it.  <attribute> is said to reference the new object.
          The new object will not have its own name; it is actually a
          nameless object.  If you assign some other value to <attribute>,
          without keeping track of the new object some other way, it will
          be hard to find again.  (The for statement will pick it up,
          however.)  Here is one application of the create statement.  The
          following types define a list.  The nodes act like "coathangers";
          the senders of the 'Attach Me' messages are pointed to by the












          nodes, which in turn point to the next node down the list.  In
          order for this to work, the nodes have to be created as they are
          needed.

          type node based on null

            next  : UNDEFINED
            refer : UNDEFINED

          end

          type list based on null

            head : UNDEFINED
            temp : UNDEFINED

          methods

            'Attach Me' : {
              create node named temp
              temp.refer := sender
              temp.next := head
              head := temp
              }

            'Display List' : {
              temp := head
              while temp do {
                'Display Self' -> temp.refer
                temp := temp.next
                }
              }

          end

          Note that the example above assumes that the senders of the
          'Attach Me' message have a 'Display Self' method which they can
          respond to.

          destroy <attribute>

          This statement performs the opposite of the create statement.  If
          <attribute> is pointing to a dynamically instantiated object, the
          object will be destroyed.  Any other attributes which happen to
          be referencing it will now evaluate to UNDEFINED.  We can extend
          the list type defined above by adding the following method to the
          list type:

          'Shrink List' : {
            if head then {
              temp := head
              head := head.next
              destroy temp
              }













          This method shrinks the list by one element by removing the top
          element.


          Keywords

          Archetype has a number of keywords which cannot be used as
          object, type, or attribute names because they evaluate to special
          values depending on their context.

          each      UNDEFINED outside of a for loop.  Within a for loop,
                    evaluates to each object in the program, starting with
                    the first one defined for the first iteration of the
                    loop and evaluating to the next one for the next
                    iteration, and so forth.
          key       Evaluates to a single keypress.  When the program tries
                    to evaluate key, execution stops until the user presses
                    a single key.  key will then evaluate to this
                    character.
          message   The last message received by the object.  Most useful
                    in a default method, where you may not know at the time
                    of writing the program which message might be invoking
                    the method.
          read      Like key, except that program execution will be
                    suspended until the user terminates their input with a
                    RETURN.  read will then evaluate to the entire line
                    that the user typed.
          self      Evaluates to the current object.  Most useful in a type
                    definition, where you are trying to code for the
                    general case.
          sender    Evaluates to the sender of message.  Since Archetype
                    does not support arguments, this is a method's only
                    link to any necessary data that the sender might have.


          Operators and Expressions

          There are exactly three forms of an expression, which can be
          represented by the following BNF:

          <expression> :==    ( <value> |
                              ( <expression> <binary operator> <expression>
                              ) | ( <unary operator> <expression> ) )

          The operators following are discussed in rough order of their
          importance and significance.

          Assignment  ( :=, +:=, -:=, *:=, /:=, &:=  )

          The assignment operator always has the form <attribute> :=
          <expression>.  If the left side does not evaluate to an existing
          attribute, a warning will be given.  If it does, then the value













          of <expression> is placed into <attribute>.  The former value of
          <attribute> is destroyed.

          The assignment operator has several cumulative forms:  +:=, -:=,
          *:=, /:=, and &:= .  These are all of the form <operator>:= , and
          have the same effect as the statement <attribute> := <attribute>
          <operator> <expression>, but are shorter and faster.  In other
          words, instead of

          a := a + 1

          write

          a +:= 1

          The value of the expression is always the final value of
          <attribute>.  Assignment operators group right to left, which is
          unusual (most Archetype operators group left to right); this is
          so that successive assignments can be made to the same value, so
          that:

          subj := dobj := verb := prep := UNDEFINED

          will set all four attributes to UNDEFINED.


          NOTE:  it is important to remember that the attribute on the left
          hand side of the assignment will receive the value of the
          expression on the right, not its unevaluated form.  What this
          means is that a statement such as

               abc := main.dobj

          will set the value of the attribute abc to the current value of
          main.dobj.  If main.dobj changes its value later, abc's value
          will not change.  This is in contrast to the practice of
          initializing an attribute with an expression, as described above
          in Object Declarations.

          Sending Messages ( ->, --> )

          These operators are always of the form <message> -> <object> or
          <message> -> <type name>, and simply send <message> to <object>.
          If <object> has a method defined for <message>, that method will
          be invoked, and the value of the expression will be the value of
          the last statement executed in the method.  If <message> is not a
          defined message, or <object> is not a defined object, the value
          of the expression is UNDEFINED, and the message is never sent.
          Note that the vocabulary of sendable messages is built from all
          the single-quoted literals in your program.  This simply means
          that you should always put messages in single quotes.

          If <message> and <object> are both valid, but <object> does not
          have a method defined for <message>, the value of the send












          expression is the word ABSENT.  Note that this is not just a
          system-generated message; an object can actually return ABSENT if
          it wishes to inform the sender that it did not "handle" the
          message.  This is most commonly used if an object had defined a
          default message, since the presence of such a method means that
          the object automatically "handles" any message given it.  For
          example, the following default method handles direction messages,
          but not any others:

          default :
            if message -> compass then
              message -> handler
            else
              ABSENT

          The pass (-->) operator invokes the appropriate method from
          <object>, but executes the method within the context of the
          current object, as though the current object were receiving the
          message.  This is always the functionality used whenever the
          receiver is a <type name>, since types do not have attributes.
          One common reason to pass a message to a type is when you are
          defining an extension to an existing method in your type, and you
          do not wish to type (or do not know) all the previous code.  When
          the player tries to drop the following object, there is first a
          condition:

          object superglue

            location : drawer
            desc     : "super glue"
            sticky   : TRUE

          methods

            'drop' :
              if location = player and sticky then
                write "I can't!  It's stuck to my fingers!"
              else
                message --> object           # do what you normally do

            'dissolve' :
              if sticky then {
                write "I dissolved the super glue."
                sticky := FALSE
                }
              else
                write "It's already dissolved."

          end

          Conditionals ( =, ~=, >, <, >=, <= )

          These are operators that compare two values and return either
          TRUE or FALSE, if their operands are comparable, or UNDEFINED if












          not.  They are used almost exclusively within if, while, and for
          statements, although they will always return their value no
          matter where they are used.

          If both operands are or can be converted to numbers, they are
          compared numerically.  If not, then they are compared as strings,
          if possible.  If they cannot be converted to strings, then the
          quantitative comparisons ( <, >, <=, >= ) will return UNDEFINED.

          The equal ( = ) and unequal ( ~= ) operators are special because
          they can compare non-numeric, non-string values to simply see if
          they are identical or not.  Even though the operators ( <=, >= )
          mean "less than or equal" and "greater than or equal"
          respectively, these cannot be used in all the same places because
          they are still checking quantitative equality.  A few examples
          will make things clearer:

          Comparison                    Result
          5 < 6                         TRUE because five is less than six.
          "mosquito" < "moth"           TRUE because "mosquito" comes
                                        before "moth" in the ASCII
                                        alphabet.
          5 < "four"                    TRUE.  Since "four" cannot be
                                        converted to a number (it is not
                                        composed of digits), 5 is converted
                                        to the string "5", and numbers
                                        always come before letters in the
                                        ASCII alphabet.
          5 < "4"                       FALSE.  "4" can be converted to the
                                        number 4; they are compared as such
                                        and 5 is greater than 4.
          "abc" = "abc"                 TRUE.
          "def" ~= "abc"                TRUE.  "def" is not "abc".
          "abcd" = "abc"                FALSE.  Two strings must be the
                                        same character for character in
                                        order to be considered equal.
          player.location = ballroom    TRUE if, indeed, the location
                                        attribute of the player object
                                        points to the ballroom object.
          7 <= 7                        TRUE.
          player.location <= ballroom   UNDEFINED.  There is no universal
                                        sense in which object references
                                        can be measured.
          UNDEFINED < 7                 UNDEFINED.  UNDEFINED has no
                                        measurable value in any sense.
          UNDEFINED = 7                 FALSE.  7 is indeed defined:  as 7.
          box.wearing = UNDEFINED       TRUE if either the wearing
                                        attribute of the box has not been
                                        defined or if it has been
                                        explicitly assigned the value of
                                        UNDEFINED.















          The Logical Operators ( and, or, not )

          These operators always return values of TRUE and FALSE; they are
          used within if, while, and for statements (although they will
          return their values in other contexts as well) to take action
          based on a number of conditions:

          and       will be TRUE if both its operands are neither FALSE,
                    ABSENT, or UNDEFINED.
          or        will be TRUE so long as at least one operand is neither
                    FALSE, ABSENT, or UNDEFINED.
          not       is unary; it returns TRUE if its operand is FALSE,
                    ABSENT, or UNDEFINED, and FALSE if not.

          Because the precedences of these operators are all beneath those
          of the comparison operators, you can form natural expressions
          without parentheses such as:

          if player.location = trapdoor_room and trapdoor.is_closed then

          If you ever get confused, however, use parentheses to ensure
          correctness.  They will not take any more memory at run time.
          However, familiarity with the operators' precedence is best of
          all, and the fewer parentheses, the more readable your code.

          The Arithmetic Operators ( +, -, *, /, ^ )

          These perform simple arithmetic on numbers or values that can be
          converted to numbers, returning the result.  They are evaluated
          as follows:

          ^         Exponentiation.  Raises its left operand to the power
                    of its right.  Performed right to left.
          *, /      Multiplication and division.  Performed left to right.
          +, -      Addition and subtraction.  Performed left to right.

          This is the normal algebraic precedence of operators.  Thus:

          3 + 5 * 2 ^ 2 = 23

          If you intend another precedence, simply use parentheses:

          ((3 + 5) * 2)^2 = 256

          The String Operators ( &, within, length, leftfrom, rightfrom )

          These operators manipulate strings.  They are defined as follows:

          &              Concatenation.  Both operands are converted to
                         strings; value is UNDEFINED if this is not
                         possible.  Returns a new string formed by
                         concatenating the right operand to the left
                         operand.  "the " & "cupboard" becomes
                         "the cupboard".












          within         Search.  Both operands are converted to strings;
                         value is UNDEFINED if this is not possible.
                         Returns an integer representing the position in
                         the second string where the first string can be
                         found.  "boar" within "cupboard" = 4.  If the
                         first string cannot be found in the second, the
                         value is 0.  "lard" within "cupboard" = 0.
          length         A unary operator expecting a single string
                         (UNDEFINED if otherwise).  Returns the number of
                         characters in the string.  length "snake" = 5,
                         length "" = 0, length player.location = UNDEFINED
                         (unless player.location is, in fact, a string).
          leftfrom       Its left operand is a string; its right operand is
                         the position in the string to take characters to
                         the "left from".  Examples:  "cupboard" leftfrom 4
                         = "cupb", "cupboard" leftfrom 10  = "cupboard",
                         and so forth.
          rightfrom      Its left operand is a string; its right operand is
                         the position in the string to take characters to
                         the "right from".  Examples:  "cupboard" rightfrom
                         4 = "board", "cupboard" rightfrom 1  = "cupboard",
                         and so forth.

          The leftfrom and rightfrom operators are a little clumsy;
          character range specification would certainly be clearer.
          However, substring operations are not all that common in
          adventure game applications.  The real reason is that the design
          I chose for my expression parser couldn't handle range
          specifications.  Someday I'll fix that.

          The Conversion Operators ( chs, numeric, string )

          These operators are unary and return some transformation of their
          operand.  They are UNDEFINED if their operand cannot be thus
          converted.

          chs       CHange Sign.  For numbers only; inverts the sign.  Its
                    alias is a minus sign ( - ) in front of a number.  In
                    other words, (5 * -3) and (5 * chs 3) are equivalent
                    internally.
          numeric   Converts a string explicitly to a number.  Its alias is
                    a plus sign ( + ) in front of the string.  In other
                    words, (numeric "453") and (+"453") are equivalent
                    internally.
          string    Converts its operand to a string if possible.  TRUE
                    becomes "TRUE", 364 becomes "364" and so forth.  Its
                    alias is an ampersand ( & ) in front of the value to
                    convert.  In other words, (string 453) and (&453) are
                    equivalent internally.

          Because all Archetype operators attempt to convert their operands
          to the necessary type anyway, these operators are largely
          unnecessary except to test to see if the conversion is possible,
          as in













          if numeric obj.attr then ...

          The Random Operator ( ? )

          This operator is unary and simply returns a random number in the
          range 1 - <operand>.  To simulate two six-sided dice rolling, for
          example, you might write

          diceroll := ?6 + ?6

          but not

          diceroll = ?6 * 2

          which would have the effect of rolling a single die and then
          doubling its value.

          The Dot Operator ( . )

          This ubiquitous operator expects an object reference on its left
          and an attribute name on its right.  If the object has defined or
          inherited an attribute by that name, it returns the value of that
          attribute.  If not, it returns UNDEFINED.  The dot operator
          groups left to right (like most operators) so that
          "player.location.location.open" refers to the "open" attribute of
          the object pointed to by the "location" attribute of the object
          pointed to by the "location" attribute of the "player" object.
          In other words, only the very leftmost operand in such a string
          will be an object reference; the rest must be attribute names.

          The "Quote Me" Operator ( >> )

          This operator, like a comment, continues from where it is used up
          to the end of the line in the source code.  Instead of commenting
          out what follows it, however, it writes it to screen, just like
          the write statement.  For blocks of text that need a particular
          kind of indentation, it is more convenient and readable than
          write because "what you see is what you get".  Also, since the
          "quote me" looks only for the end of the line, any characters can
          follow it, including quotation marks and space, without
          prematurely ending the expression.

          Like the write statements, output from the "quote me" operator is
          also "paged" (paused for the user every 23 lines).  Lines
          exceeding 75 characters or so will also be word-wrapped.

          Precedence of Operators

          Any expression enclosed in parentheses is evaluated first,
          beginning with the innermost set of parentheses.  Where
          parentheses are not used, operators with the highest precedence
          are evaluated first.













          Operator  Precedence   Grouping
          .         13           left to right

          chs       12           unary
          numeric   12           unary
          string    12           unary
          random    12           unary
          length    12           unary

          ^         11           right to left

          *         10           left to right
          /         10           left to right

          +         9            left to right
          -         9            left to right
          &         9            left to right

          within    8            left to right

          leftfrom  7            left to right
          rightfrom 7            left to right

          ->        6            left to right
          -->       6            left to right

          =         5            left to right
          ~=        5            left to right
          >         5            left to right
          <         5            left to right
          >=        5            left to right
          <=        5            left to right

          not       4            -
          and       3            left to right
          or        2            left to right

          *:=       1            right to left
          /:=       1            right to left
          +:=       1            right to left
          -:=       1            right to left
          &:=       1            right to left
          :=        1            right to left

          Data Types

          Strings
          Strings are represented within double quote marks (").  If the
          string is supposed to contain a double quote mark itself, precede
          the double quote with a backslash.  Doing so indicates that the
          double quote is to be part of the string, not the end of it.  For
          example,

          write "My name is \"Bob\""













          produces

          My name is "Bob"

          Two backslashes in a row (\\) become a single backslash within a
          string.

          There are four other characters that, when preceded by a
          backslash, produce a "special character" within a string:

               \b   The backspace character
               \e   The escape character (CHR(27))
               \t   The tab character
               \n   The newline (a carriage return/line feed)

          However, be careful of using these characters within your
          Archetype strings because as of the writing of this manual, the
          word-wrap and paging algorithms do not recognize them!  In other
          words, if you have a very long piece of text containing \n's, the
          output will not be stopped at the 23rd line.  The output engine
          does not realize that the \n finished a line.

          Any other character preceded by a backslash becomes simply that
          character without the backslash.  \m becomes m, for example.

          Also note that no string can ever exceed 256 characters is
          length.  This unfortunate limitation is a consequence of the fact
          that Archetype was programmed in Turbo Pascal, which also has
          this limitation.  (And the fact that the programmer was too lazy
          to define his own string type.)

          Numbers
          Archetype supports four-byte integers only.  No real numbers, no
          floating-point arithmetic.  Numbers therefore have the range of
          -2^32 to 2^32 - 1.

          Messages
          A message is enclosed in single quotes as opposed to a string,
          which is enclosed in double quotes.  Any message can be converted
          to a string, but a string can only be converted to a message if
          the message appears somewhere else in your program in single
          quotes.  The reason for this is that messages are stored in the
          .ACX file as a single number.  This improves speed (and memory
          usage) a great deal and encourages the use of a message as a
          unique constant instead of as a free-form string.  Therefore you
          do not have to worry that a long message such as
          'DEBUG EXPRESSIONS' will be slower to send and receive than 'AB';
          it won't.

          It is an Archetype convention to use all caps in a system message
          and all lowercase if the message represents a word from the
          parser.  If you define your own messages for simple inter-object













          communication, the convention is to capitalize the first letter,
          as in 'Repeated Action'.

          Objects
          These are declared in the source code or created with the create
          statement.  They cannot be converted to and from any other data
          type.  They are valid on the right hand sides of the ->, -->, and
          := operators, and on the left hand side of the dot (.) operator.

          Types
          These are only declared within the source code; they cannot be
          dynamically created.  There is little you can do at run-time with
          a type object; you are not allowed to reference its attributes
          with the dot (.) operator, and you cannot send messages to it
          with -> .  You can, however, pass messages to it with -->.  In
          fact if you use -> with a type on the right hand side it will be
          demoted to -->.  This can be useful when an object has redefined
          a method but needs to invoke the original.

          Keywords
          Any identifier not declared as an object, type, or attribute is a
          keyword; a state value.  These can be assigned and compared
          against but have no meaningful conversion to strings or numbers.
          If the /K option is used with the CREATE program, all keywords
          must be declared with the keyword statement.  This helps catch
          spelling errors that might otherwise be very difficult to debug,
          particularly in a large program.


          The System Object

          As mentioned earlier, there is a special object name, "main", to
          which Archetype sends the message 'START' when the interpreter
          starts up.  The second special object name in Archetype is
          "system", and refers to the system object that is part of the
          interpreter.  This object performs parsing and parsing-related
          operations, sorting of lists of strings, debugging, and
          save/restore of the state of the interpreter.

          The system object is also different in one major respect from all
          other objects in Archetype:  it is the only object that can
          receive free-form strings as messages.  As mentioned before, all
          other Archetype objects can only receive strings which appear
          elsewhere as messages.

          Generally, the system object is sent messages which put it into
          one state or another, and then all strings it receives until
          another special string are considered data.


















          Messages Pertaining to Parsing

          'ABBR'
               Sets the abbreviation level of the parsing.  This puts the
               system object into a state where it prepares to receive one
               numeric message; then it returns to idle.  Set the
               abbreviation as follows:
               'ABBR' -> system
               4      -> system
               This would have the effect of turning "calculator" into
               "calc", both when the vocabulary is built and when player
               commands are being parsed.
          'INIT PARSER'
               Initializes the parser and then puts the system into its
               vocabulary building state.  See 'OPEN PARSER'.
          'OPEN PARSER'
               Puts the system into vocabularly building state.  Does not
               wipe out the vocabularly already built.  In this state, all
               messages are assumed to be names of verbs or nouns, except
               the special messages 'VERB LIST', 'NOUN LIST', or 'CLOSE
               PARSER'.  The object which the name refers to is assumed to
               be the sender.  Multiple names for a single object can be
               separated by vertical bars; thus, the message
               'rock|stone|rowlirag' is considered to be three synonymous
               names for the sender of the message.
               'VERB LIST'
                    All subsequent messages are considered to be verb names
                    or synonyms.
               'NOUN LIST'
                    All subsequent messages are considered to be nouns.
                    Either 'VERB LIST' or 'NOUN LIST' must be sent to the
                    system after an 'OPEN PARSER'; there is no default.
               'CLOSE PARSER'
                    Stop receiving vocabulary items; return to idle state.
          'ROLL CALL'
               To help it resolve ambiguities, the system needs to know
               which objects are "nearby" the player.  In other words, if
               there are six different objects defined in a game which are
               all called "button", and the player types "push button", the
               button indicated is probably the one nearby.  'ROLL CALL'
               erases the current sense of what is nearby and prepares the
               system to receive 'PRESENT' messages.
          'PRESENT'
               The sender is assumed to be a "nearby" object, and will
               continue to be considered such until the next 'ROLL CALL'
               message.
          'PLAYER CMD'
               Like 'ABBR', this takes a single "argument":  it puts the
               system into a state where the next message received is
               considered data, then returns to idle.  In this case the
               message is the exact string the player typed in.
          'NORMALIZE'
               Returns the normalized player command as of the last
               'PLAYER CMD' message.  This string will be in all lowercase,












               with words separated by a single space, all punctuation
               removed except apostrophes and hyphens, and will always have
               one trailing space.  If the player typed nothing at all,
               'NORMALIZE' will return a single space.
          'PARSE'
               Parses the last string received through 'PLAYER CMD'.  Does
               not return anything, simply performs the action of parsing.
               Gets rid of all instances of the words "a", "an", or "the"
               before parsing.
          'NEXT OBJECT'
               This is how the system object indicates which objects
               matched what words in the player's string.  The string from
               'PLAYER CMD' will have been turned into a sequence of
               objects.  This is possible because even verbs have objects
               associated with them.  'NEXT OBJECT' returns the next object
               in the list, from left to right.  If the word or phrase
               didn't translate, 'NEXT OBJECT' returns the string that
               failed to parse.  When the list of objects is exhausted,
               'NEXT OBJECT' returns UNDEFINED, and will do so until the
               'PARSE' message is sent again.
          'WHICH OBJECT'
               Used to look up an object, given its parse name.  Like the
               'PLAYER CMD' message, this message puts the system into a
               state where it expects that the very next message received
               will be a string with the name of an object.  When that name
               is received, the system goes back to idle state and returns
               a pointer to the object with that name, or UNDEFINED if no
               such object exists.  The lookup is based on the vocabulary
               given to the system since the last 'CLOSE PARSER' message.

          Messages Pertaining to Sorting

          'INIT SORTER'
               Initializes the system object's sorting algorithm, which is
               the heap sort.  It then opens the sorter, as if an
               'OPEN SORTER' message was sent.
          'OPEN SORTER'
               Prepares the system object to receive data.  Every string
               sent to the system is dropped on the heap as sortable data
               from now on until the message 'CLOSE SORTER'.
               'CLOSE SORTER'
                    Stop receiving sort data; return to idle state.
          'NEXT SORTED'
               Returns the next string in a sorted list.  Sorting is always
               done in ascending order, so the string returned will have
               been the lexically "smallest" string since the last
               'CLOSE SORTER' message.  After returning the string, it is
               removed from the heap, and when the heap is empty,
               'NEXT SORTED' returns UNDEFINED.

          Messages Pertaining to Debugging

          These three message are all "toggles":  sending the message turns
          the associated property on; sending them again turns it off.  For












          these messages to be useful at all, the Archetype code must have
          been CREATEd with debugging information.

          'DEBUG MESSAGES'
               Prints out to the screen every message that is sent; who
               it's being sent to and who is sending it.
          'DEBUG EXPRESSIONS'
               Prints to the screen every expression that is going to be
               evaluated and what it evaluates to.
          'DEBUG STATEMENTS'
               Prints to the screen every statement that is going to be
               executed.

          The following messages are not toggles; they are simple actions
          that only report the state of the interpreter.

          'DEBUG MEMORY'
               Outputs a short report on the number of bytes free and what
               the maximum memory request is allowed to be.
          'FREE MEMORY'
               This is a query, not a command.  Returns an integer which is
               the number of bytes free for Archetype to use.

          Messages Pertaining to the Interpreter State

          Both of these messages behave like 'WHICH OBJECT', above:  send
          the message to the system and follow it with the name of the file
          to use as the state file.  Both messages return UNDEFINED if the
          file cannot be opened.

          'SAVE STATE'
               Saves the state of the interpreter to the given file name.
               This includes the values of all attributes and any
               dynamically created objects.  It does not include the
               assembled vocabulary.  Returns TRUE when done.
          'LOAD STATE'
               Loads all of the information in the state file with the
               given file name.  It will not load, however, if the state
               file was not written by the exact same .ACX file.  In other
               words, if you write out a state file from within a program,
               and then recompile that Archetype program, the new .ACX file
               will not be able to load the previous state file.  If this
               happens, the message will return FALSE; otherwise it will
               load the information and return TRUE.






















          Appendix A

          Backus-Naur Form

          The Backus-Naur Form (or BNF) is a way of representing syntax.
          In this manual, the meaning of various symbols is as follows:

          boldface       - the use of boldface type means that this should
                         be typed literally.
          <words>        - something in <angle brackets> is symbolic; it
                         refers to something else.
          ( )            - parentheses mean that everything inside is to be
                         considered one thing.
          |              - the vertical bar is used to separate
                         alternatives.  For example, (a | b | c) means
                         exactly one of a, b, or c.  (a  (b | c)) means ab
                         or ac.
          [ ]            - square brackets mean that what is inside is
                         optional; you can use it or not.  (a [b | c])
                         means either a, ab, or ac.
          *              - the asterisk means "zero or more" of what it
                         follows.  ab* means a, ab, abb, or abbb, and so
                         on.

          Example:

          The following BNF:
          ( write | writes | stop ) [ <expression> (, <expression> )* ]

          can be expressed in English as:

          Either the word write, writes, or stop, optionally followed by an
          expression and zero or more instances of a comma followed by
          another expression.

          Some of the valid statements you can construct (leaving
          <expression> within angle brackets) from this BNF are:

          write
          stop <expression>
          writes <expression>, <expression>
          write <expression>, <expression>, <expression>
          stop

















