Implement a Makefile for Inform.
[inform.git] / src / objects.c
diff --git a/src/objects.c b/src/objects.c
new file mode 100644 (file)
index 0000000..88b1b6a
--- /dev/null
@@ -0,0 +1,2269 @@
+/* ------------------------------------------------------------------------- */
+/*   "objects" :  [1] the object-maker, which constructs objects and enters  */
+/*                    them into the tree, given a low-level specification;   */
+/*                                                                           */
+/*                [2] the parser of Object/Nearby/Class directives, which    */
+/*                    checks syntax and translates such directives into      */
+/*                    specifications for the object-maker.                   */
+/*                                                                           */
+/* Copyright (c) Graham Nelson 1993 - 2018                                   */
+/*                                                                           */
+/* This file is part of Inform.                                              */
+/*                                                                           */
+/* Inform is free software: you can redistribute it and/or modify            */
+/* it under the terms of the GNU General Public License as published by      */
+/* the Free Software Foundation, either version 3 of the License, or         */
+/* (at your option) any later version.                                       */
+/*                                                                           */
+/* Inform is distributed in the hope that it will be useful,                 */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
+/* GNU General Public License for more details.                              */
+/*                                                                           */
+/* You should have received a copy of the GNU General Public License         */
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
+/*                                                                           */
+/* ------------------------------------------------------------------------- */
+
+#include "header.h"
+
+/* ------------------------------------------------------------------------- */
+/*   Objects.                                                                */
+/* ------------------------------------------------------------------------- */
+
+int no_objects;                        /* Number of objects made so far      */
+
+static int no_embedded_routines;       /* Used for naming routines which
+                                          are given as property values: these
+                                          are called EmbeddedRoutine__1, ... */
+
+static fpropt full_object;             /* "fpropt" is a typedef for a struct
+                                          containing an array to hold the
+                                          attribute and property values of
+                                          a single object.  We only keep one
+                                          of these, for the current object
+                                          being made, and compile it into
+                                          Z-machine tables when each object
+                                          definition is complete, since
+                                          sizeof(fpropt) is about 6200 bytes */
+static fproptg full_object_g;          /* Equivalent for Glulx. This object
+                                          is very small, since the large arrays
+                                          are allocated dynamically by the
+                                          Glulx compiler                     */
+static char shortname_buffer[766];     /* Text buffer to hold the short name
+                                          (which is read in first, but
+                                          written almost last)               */
+static int parent_of_this_obj;
+
+static char *classname_text, *objectname_text;
+                                       /* For printing names of embedded
+                                          routines only                      */
+
+/* ------------------------------------------------------------------------- */
+/*   Classes.                                                                */
+/* ------------------------------------------------------------------------- */
+/*   Arrays defined below:                                                   */
+/*                                                                           */
+/*    int32 class_begins_at[n]            offset of properties block for     */
+/*                                        nth class (always an offset        */
+/*                                        inside the properties_table)       */
+/*    int   classes_to_inherit_from[]     The list of classes to inherit     */
+/*                                        from as taken from the current     */
+/*                                        Nearby/Object/Class definition     */
+/*    int   class_object_numbers[n]       The number of the prototype-object */
+/*                                        for the nth class                  */
+/* ------------------------------------------------------------------------- */
+
+int        no_classes;                 /* Number of class defns made so far  */
+
+static int current_defn_is_class,      /* TRUE if current Nearby/Object/Class
+                                          defn is in fact a Class definition */
+           no_classes_to_inherit_from; /* Number of classes in the list
+                                          of classes to inherit in the
+                                          current Nearby/Object/Class defn   */
+
+/* ------------------------------------------------------------------------- */
+/*   Making attributes and properties.                                       */
+/* ------------------------------------------------------------------------- */
+
+int no_attributes,                 /* Number of attributes defined so far    */
+    no_properties;                 /* Number of properties defined so far,
+                                      plus 1 (properties are numbered from
+                                      1 and Inform creates "name" and two
+                                      others itself, so the variable begins
+                                      the compilation pass set to 4)         */
+
+static void trace_s(char *name, int32 number, int f)
+{   if (!printprops_switch) return;
+    printf("%s  %02ld  ",(f==0)?"Attr":"Prop",(long int) number);
+    if (f==0) printf("  ");
+    else      printf("%s%s",(prop_is_long[number])?"L":" ",
+                            (prop_is_additive[number])?"A":" ");
+    printf("  %s\n",name);
+}
+
+extern void make_attribute(void)
+{   int i; char *name;
+    debug_location_beginning beginning_debug_location =
+        get_token_location_beginning();
+
+ if (!glulx_mode) { 
+    if (no_attributes==((version_number==3)?32:48))
+    {   discard_token_location(beginning_debug_location);
+        if (version_number==3)
+            error("All 32 attributes already declared (compile as Advanced \
+game to get an extra 16)");
+        else
+            error("All 48 attributes already declared");
+        panic_mode_error_recovery();
+        put_token_back();
+        return;
+    }
+ }
+ else {
+    if (no_attributes==NUM_ATTR_BYTES*8) {
+      discard_token_location(beginning_debug_location);
+      error_numbered(
+        "All attributes already declared -- increase NUM_ATTR_BYTES to use \
+more than", 
+        NUM_ATTR_BYTES*8);
+      panic_mode_error_recovery(); 
+      put_token_back();
+      return;
+    }
+ }
+
+    get_next_token();
+    i = token_value; name = token_text;
+    if ((token_type != SYMBOL_TT) || (!(sflags[i] & UNKNOWN_SFLAG)))
+    {   discard_token_location(beginning_debug_location);
+        ebf_error("new attribute name", token_text);
+        panic_mode_error_recovery(); 
+        put_token_back();
+        return;
+    }
+
+    directive_keywords.enabled = TRUE;
+    get_next_token();
+    directive_keywords.enabled = FALSE;
+
+    if ((token_type == DIR_KEYWORD_TT) && (token_value == ALIAS_DK))
+    {   get_next_token();
+        if (!((token_type == SYMBOL_TT)
+              && (stypes[token_value] == ATTRIBUTE_T)))
+        {   discard_token_location(beginning_debug_location);
+            ebf_error("an existing attribute name after 'alias'",
+                token_text);
+            panic_mode_error_recovery();
+            put_token_back();
+            return;
+        }
+        assign_symbol(i, svals[token_value], ATTRIBUTE_T);
+        sflags[token_value] |= ALIASED_SFLAG;
+        sflags[i] |= ALIASED_SFLAG;
+    }
+    else
+    {   assign_symbol(i, no_attributes++, ATTRIBUTE_T);
+        put_token_back();
+    }
+
+    if (debugfile_switch)
+    {   debug_file_printf("<attribute>");
+        debug_file_printf("<identifier>%s</identifier>", name);
+        debug_file_printf("<value>%d</value>", svals[i]);
+        write_debug_locations(get_token_location_end(beginning_debug_location));
+        debug_file_printf("</attribute>");
+    }
+
+    trace_s(name, svals[i], 0);
+    return;
+}
+
+extern void make_property(void)
+{   int32 default_value, i;
+    int additive_flag=FALSE; char *name;
+    assembly_operand AO;
+    debug_location_beginning beginning_debug_location =
+        get_token_location_beginning();
+
+    if (!glulx_mode) {
+        if (no_properties==((version_number==3)?32:64))
+        {   discard_token_location(beginning_debug_location);
+            if (version_number==3)
+                error("All 30 properties already declared (compile as \
+Advanced game to get an extra 62)");
+            else
+                error("All 62 properties already declared");
+            panic_mode_error_recovery();
+            put_token_back();
+            return;
+        }
+    }
+    else {
+        if (no_properties==INDIV_PROP_START) {
+            discard_token_location(beginning_debug_location);
+            error_numbered("All properties already declared -- max is",
+                INDIV_PROP_START);
+            panic_mode_error_recovery(); 
+            put_token_back();
+            return;
+        }
+    }
+
+    do
+    {   directive_keywords.enabled = TRUE;
+        get_next_token();
+        if ((token_type == DIR_KEYWORD_TT) && (token_value == LONG_DK))
+            obsolete_warning("all properties are now automatically 'long'");
+        else
+        if ((token_type == DIR_KEYWORD_TT) && (token_value == ADDITIVE_DK))
+            additive_flag = TRUE;
+        else break;
+    } while (TRUE);
+
+    put_token_back();
+    directive_keywords.enabled = FALSE;
+    get_next_token();
+
+    i = token_value; name = token_text;
+    if ((token_type != SYMBOL_TT) || (!(sflags[i] & UNKNOWN_SFLAG)))
+    {   discard_token_location(beginning_debug_location);
+        ebf_error("new property name", token_text);
+        panic_mode_error_recovery();
+        put_token_back();
+        return;
+    }
+
+    directive_keywords.enabled = TRUE;
+    get_next_token();
+    directive_keywords.enabled = FALSE;
+
+    if (strcmp(name+strlen(name)-3, "_to") == 0) sflags[i] |= STAR_SFLAG;
+
+    if ((token_type == DIR_KEYWORD_TT) && (token_value == ALIAS_DK))
+    {   discard_token_location(beginning_debug_location);
+        if (additive_flag)
+        {   error("'alias' incompatible with 'additive'");
+            panic_mode_error_recovery();
+            put_token_back();
+            return;
+        }
+        get_next_token();
+        if (!((token_type == SYMBOL_TT)
+            && (stypes[token_value] == PROPERTY_T)))
+        {   ebf_error("an existing property name after 'alias'",
+                token_text);
+            panic_mode_error_recovery();
+            put_token_back();
+            return;
+        }
+
+        assign_symbol(i, svals[token_value], PROPERTY_T);
+        trace_s(name, svals[i], 1);
+        sflags[token_value] |= ALIASED_SFLAG;
+        sflags[i] |= ALIASED_SFLAG;
+        return;
+    }
+
+    default_value = 0;
+    put_token_back();
+
+    if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
+    {   AO = parse_expression(CONSTANT_CONTEXT);
+        default_value = AO.value;
+        if (AO.marker != 0)
+            backpatch_zmachine(AO.marker, PROP_DEFAULTS_ZA, 
+                (no_properties-1) * WORDSIZE);
+    }
+
+    prop_default_value[no_properties] = default_value;
+    prop_is_long[no_properties] = TRUE;
+    prop_is_additive[no_properties] = additive_flag;
+
+    assign_symbol(i, no_properties++, PROPERTY_T);
+
+    if (debugfile_switch)
+    {   debug_file_printf("<property>");
+        debug_file_printf("<identifier>%s</identifier>", name);
+        debug_file_printf("<value>%d</value>", svals[i]);
+        write_debug_locations
+            (get_token_location_end(beginning_debug_location));
+        debug_file_printf("</property>");
+    }
+
+    trace_s(name, svals[i], 1);
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Properties.                                                             */
+/* ------------------------------------------------------------------------- */
+
+int32 *prop_default_value;             /* Default values for properties      */
+int   *prop_is_long,                   /* Property modifiers, TRUE or FALSE:
+                                          "long" means "never write a 1-byte
+                                          value to this property", and is an
+                                          obsolete feature: since Inform 5
+                                          all properties have been "long"    */
+      *prop_is_additive;               /* "additive" means that values
+                                          accumulate rather than erase each
+                                          other during class inheritance     */
+char *properties_table;                /* Holds the table of property values
+                                          (holding one block for each object
+                                          and coming immediately after the
+                                          object tree in Z-memory)           */
+int properties_table_size;             /* Number of bytes in this table      */
+
+/* ------------------------------------------------------------------------- */
+/*   Individual properties                                                   */
+/*                                                                           */
+/*   Each new i.p. name is given a unique number.  These numbers start from  */
+/*   72, since 0 is reserved as a null, 1 to 63 refer to common properties   */
+/*   and 64 to 71 are kept for methods of the metaclass Class (for example,  */
+/*   64 is "create").                                                        */
+/*                                                                           */
+/*   An object provides individual properties by having property 3 set to a  */
+/*   non-zero value, which must be a byte address of a table in the form:    */
+/*                                                                           */
+/*       <record-1> ... <record-n> 00 00                                     */
+/*                                                                           */
+/*   where a <record> looks like                                             */
+/*                                                                           */
+/*       <identifier>              <size>  <up to 255 bytes of data>         */
+/*       or <identifier + 0x8000>                                            */
+/*       ----- 2 bytes ----------  1 byte  <size> number of bytes            */
+/*                                                                           */
+/*   The <identifier> part is the number allocated to the name of what is    */
+/*   being provided.  The top bit of this word is set to indicate that       */
+/*   although the individual property is being provided, it is provided      */
+/*   only privately (so that it is inaccessible except to the object's own   */
+/*   embedded routines).                                                     */
+/*                                                                           */
+/*   In Glulx: i-props are numbered from INDIV_PROP_START+8 up. And all      */
+/*   properties, common and individual, are stored in the same table.        */
+/* ------------------------------------------------------------------------- */
+
+       int no_individual_properties;   /* Actually equal to the next
+                                          identifier number to be allocated,
+                                          so this is initially 72 even though
+                                          none have been made yet.           */
+static int individual_prop_table_size; /* Size of the table of individual
+                                          properties so far for current obj  */
+       uchar *individuals_table;       /* Table of records, each being the
+                                          i.p. table for an object           */
+       int i_m;                        /* Write mark position in the above   */
+       int individuals_length;         /* Extent of individuals_table        */
+
+/* ------------------------------------------------------------------------- */
+/*   Arrays used by this file                                                */
+/* ------------------------------------------------------------------------- */
+
+objecttz     *objectsz;                /* Z-code only                        */
+objecttg     *objectsg;                /* Glulx only                         */
+uchar        *objectatts;              /* Glulx only                         */
+static int   *classes_to_inherit_from;
+int          *class_object_numbers;
+int32        *class_begins_at;
+
+
+/* ------------------------------------------------------------------------- */
+/*   Tracing for compiler maintenance                                        */
+/* ------------------------------------------------------------------------- */
+
+extern void list_object_tree(void)
+{   int i;
+    printf("obj   par nxt chl   Object tree:\n");
+    for (i=0; i<no_objects; i++)
+        printf("%3d   %3d %3d %3d\n",
+            i+1,objectsz[i].parent,objectsz[i].next, objectsz[i].child);
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Object and class manufacture begins here.                               */
+/*                                                                           */
+/*   These definitions have headers (parsed far, far below) and a series     */
+/*   of segments, introduced by keywords and optionally separated by commas. */
+/*   Each segment has its own parsing routine.  Note that when errors are    */
+/*   detected, parsing continues rather than being abandoned, which assists  */
+/*   a little in "error recovery" (i.e. in stopping lots more errors being   */
+/*   produced for essentially the same mistake).                             */
+/* ------------------------------------------------------------------------- */
+
+/* ========================================================================= */
+/*   [1]  The object-maker: builds an object from a specification, viz.:     */
+/*                                                                           */
+/*           full_object,                                                    */
+/*           shortname_buffer,                                               */
+/*           parent_of_this_obj,                                             */
+/*           current_defn_is_class (flag)                                    */
+/*           classes_to_inherit_from[], no_classes_to_inherit_from,          */
+/*           individual_prop_table_size (to date  )                          */
+/*                                                                           */
+/*   For efficiency's sake, the individual properties table has already been */
+/*   created (as far as possible, i.e., all except for inherited individual  */
+/*   properties); unless the flag is clear, in which case the actual         */
+/*   definition did not specify any individual properties.                   */
+/* ========================================================================= */
+/*   Property inheritance from classes.                                      */
+/* ------------------------------------------------------------------------- */
+
+static void property_inheritance_z(void)
+{
+    /*  Apply the property inheritance rules to full_object, which should
+        initially be complete (i.e., this routine takes place after the whole
+        Nearby/Object/Class definition has been parsed through).
+
+        On exit, full_object contains the final state of the properties to
+        be written.                                                          */
+
+    int i, j, k, kmax, class, mark,
+        prop_number, prop_length, prop_in_current_defn;
+    uchar *class_prop_block;
+
+    ASSERT_ZCODE();
+
+    for (class=0; class<no_classes_to_inherit_from; class++)
+    {
+        j=0;
+        mark = class_begins_at[classes_to_inherit_from[class]-1];
+        class_prop_block = (uchar *) (properties_table + mark);
+
+        while (class_prop_block[j]!=0)
+        {   if (version_number == 3)
+            {   prop_number = class_prop_block[j]%32;
+                prop_length = 1 + class_prop_block[j++]/32;
+            }
+            else
+            {   prop_number = class_prop_block[j]%64;
+                prop_length = 1 + class_prop_block[j++]/64;
+                if (prop_length > 2)
+                    prop_length = class_prop_block[j++]%64;
+            }
+
+            /*  So we now have property number prop_number present in the
+                property block for the class being read: its bytes are
+
+                class_prop_block[j, ..., j + prop_length - 1]
+
+                Question now is: is there already a value given in the
+                current definition under this property name?                 */
+
+            prop_in_current_defn = FALSE;
+
+            kmax = full_object.l;
+
+            for (k=0; k<kmax; k++)
+                if (full_object.pp[k].num == prop_number)
+                {   prop_in_current_defn = TRUE;
+
+                    /*  (Note that the built-in "name" property is additive) */
+
+                    if ((prop_number==1) || (prop_is_additive[prop_number]))
+                    {
+                        /*  The additive case: we accumulate the class
+                            property values onto the end of the full_object
+                            property                                         */
+
+                        for (i=full_object.pp[k].l;
+                             i<full_object.pp[k].l+prop_length/2; i++)
+                        {   if (i >= 32)
+                            {   error("An additive property has inherited \
+so many values that the list has overflowed the maximum 32 entries");
+                                break;
+                            }
+                            full_object.pp[k].ao[i].value = mark + j;
+                            j += 2;
+                            full_object.pp[k].ao[i].marker = INHERIT_MV;
+                            full_object.pp[k].ao[i].type = LONG_CONSTANT_OT;
+                        }
+                        full_object.pp[k].l += prop_length/2;
+                    }
+                    else
+                        /*  The ordinary case: the full_object property
+                            values simply overrides the class definition,
+                            so we skip over the values in the class table    */
+
+                        j+=prop_length;
+
+                    if (prop_number==3)
+                    {   int y, z, class_block_offset;
+                        uchar *p;
+
+                        /*  Property 3 holds the address of the table of
+                            instance variables, so this is the case where
+                            the object already has instance variables in its
+                            own table but must inherit some more from the
+                            class  */
+
+                        class_block_offset = class_prop_block[j-2]*256
+                                             + class_prop_block[j-1];
+
+                        p = individuals_table + class_block_offset;
+                        z = class_block_offset;
+                        while ((p[0]!=0)||(p[1]!=0))
+                        {   int already_present = FALSE, l;
+                            for (l = full_object.pp[k].ao[0].value; l < i_m;
+                                 l = l + 3 + individuals_table[l + 2])
+                                if (individuals_table[l] == p[0]
+                                    && individuals_table[l + 1] == p[1])
+                                {   already_present = TRUE; break;
+                                }
+                            if (already_present == FALSE)
+                            {   if (module_switch)
+                                    backpatch_zmachine(IDENT_MV,
+                                        INDIVIDUAL_PROP_ZA, i_m);
+                                if (i_m+3+p[2] > MAX_INDIV_PROP_TABLE_SIZE)
+                                    memoryerror("MAX_INDIV_PROP_TABLE_SIZE",
+                                        MAX_INDIV_PROP_TABLE_SIZE);
+                                individuals_table[i_m++] = p[0];
+                                individuals_table[i_m++] = p[1];
+                                individuals_table[i_m++] = p[2];
+                                for (y=0;y < p[2]/2;y++)
+                                {   individuals_table[i_m++] = (z+3+y*2)/256;
+                                    individuals_table[i_m++] = (z+3+y*2)%256;
+                                    backpatch_zmachine(INHERIT_INDIV_MV,
+                                        INDIVIDUAL_PROP_ZA, i_m-2);
+                                }
+                            }
+                            z += p[2] + 3;
+                            p += p[2] + 3;
+                        }
+                        individuals_length = i_m;
+                    }
+
+                    /*  For efficiency we exit the loop now (this property
+                        number has been dealt with)                          */
+
+                    break;
+                }
+
+            if (!prop_in_current_defn)
+            {
+                /*  The case where the class defined a property which wasn't
+                    defined at all in full_object: we copy out the data into
+                    a new property added to full_object                      */
+
+                k=full_object.l++;
+                full_object.pp[k].num = prop_number;
+                full_object.pp[k].l = prop_length/2;
+                for (i=0; i<prop_length/2; i++)
+                {   full_object.pp[k].ao[i].value = mark + j;
+                    j+=2;
+                    full_object.pp[k].ao[i].marker = INHERIT_MV;
+                    full_object.pp[k].ao[i].type = LONG_CONSTANT_OT;
+                }
+
+                if (prop_number==3)
+                {   int y, z, class_block_offset;
+                    uchar *p;
+
+                    /*  Property 3 holds the address of the table of
+                        instance variables, so this is the case where
+                        the object had no instance variables of its own
+                        but must inherit some more from the class  */
+
+                    if (individual_prop_table_size++ == 0)
+                    {   full_object.pp[k].num = 3;
+                        full_object.pp[k].l = 1;
+                        full_object.pp[k].ao[0].value
+                            = individuals_length;
+                        full_object.pp[k].ao[0].marker = INDIVPT_MV;
+                        full_object.pp[k].ao[0].type = LONG_CONSTANT_OT;
+                        i_m = individuals_length;
+                    }
+                    class_block_offset = class_prop_block[j-2]*256
+                                         + class_prop_block[j-1];
+
+                    p = individuals_table + class_block_offset;
+                    z = class_block_offset;
+                    while ((p[0]!=0)||(p[1]!=0))
+                    {   if (module_switch)
+                        backpatch_zmachine(IDENT_MV, INDIVIDUAL_PROP_ZA, i_m);
+                        if (i_m+3+p[2] > MAX_INDIV_PROP_TABLE_SIZE)
+                            memoryerror("MAX_INDIV_PROP_TABLE_SIZE",
+                                MAX_INDIV_PROP_TABLE_SIZE);
+                        individuals_table[i_m++] = p[0];
+                        individuals_table[i_m++] = p[1];
+                        individuals_table[i_m++] = p[2];
+                        for (y=0;y < p[2]/2;y++)
+                        {   individuals_table[i_m++] = (z+3+y*2)/256;
+                            individuals_table[i_m++] = (z+3+y*2)%256;
+                            backpatch_zmachine(INHERIT_INDIV_MV,
+                                INDIVIDUAL_PROP_ZA, i_m-2);
+                        }
+                        z += p[2] + 3;
+                        p += p[2] + 3;
+                    }
+                    individuals_length = i_m;
+                }
+            }
+        }
+    }
+
+    if (individual_prop_table_size > 0)
+    {
+        if (i_m+2 > MAX_INDIV_PROP_TABLE_SIZE)
+            memoryerror("MAX_INDIV_PROP_TABLE_SIZE",
+                MAX_INDIV_PROP_TABLE_SIZE);
+
+        individuals_table[i_m++] = 0;
+        individuals_table[i_m++] = 0;
+        individuals_length += 2;
+    }
+}
+
+static void property_inheritance_g(void)
+{
+  /*  Apply the property inheritance rules to full_object, which should
+      initially be complete (i.e., this routine takes place after the whole
+      Nearby/Object/Class definition has been parsed through).
+      
+      On exit, full_object contains the final state of the properties to
+      be written. */
+
+  int i, j, k, class, num_props,
+    prop_number, prop_length, prop_flags, prop_in_current_defn;
+  int32 mark, prop_addr;
+  uchar *cpb, *pe;
+
+  ASSERT_GLULX();
+
+  for (class=0; class<no_classes_to_inherit_from; class++) {
+    mark = class_begins_at[classes_to_inherit_from[class]-1];
+    cpb = (uchar *) (properties_table + mark);
+    /* This now points to the compiled property-table for the class.
+       We'll have to go through and decompile it. (For our sins.) */
+    num_props = ReadInt32(cpb);
+    for (j=0; j<num_props; j++) {
+      pe = cpb + 4 + j*10;
+      prop_number = ReadInt16(pe);
+      pe += 2;
+      prop_length = ReadInt16(pe);
+      pe += 2;
+      prop_addr = ReadInt32(pe);
+      pe += 4;
+      prop_flags = ReadInt16(pe);
+      pe += 2;
+
+      /*  So we now have property number prop_number present in the
+          property block for the class being read. Its bytes are
+          cpb[prop_addr ... prop_addr + prop_length - 1]
+          Question now is: is there already a value given in the
+          current definition under this property name? */
+
+      prop_in_current_defn = FALSE;
+
+      for (k=0; k<full_object_g.numprops; k++) {
+        if (full_object_g.props[k].num == prop_number) {
+          prop_in_current_defn = TRUE;
+          break;
+        }
+      }
+
+      if (prop_in_current_defn) {
+        if ((prop_number==1)
+          || (prop_number < INDIV_PROP_START 
+            && prop_is_additive[prop_number])) {
+          /*  The additive case: we accumulate the class
+              property values onto the end of the full_object
+              properties. Remember that k is still the index number
+              of the first prop-block matching our property number. */
+          int prevcont;
+          if (full_object_g.props[k].continuation == 0) {
+            full_object_g.props[k].continuation = 1;
+            prevcont = 1;
+          }
+          else {
+            prevcont = full_object_g.props[k].continuation;
+            for (k++; k<full_object_g.numprops; k++) {
+              if (full_object_g.props[k].num == prop_number) {
+                prevcont = full_object_g.props[k].continuation;
+              }
+            }
+          }
+          k = full_object_g.numprops++;
+          full_object_g.props[k].num = prop_number;
+          full_object_g.props[k].flags = 0;
+          full_object_g.props[k].datastart = full_object_g.propdatasize;
+          full_object_g.props[k].continuation = prevcont+1;
+          full_object_g.props[k].datalen = prop_length;
+          if (full_object_g.propdatasize + prop_length 
+            > MAX_OBJ_PROP_TABLE_SIZE) {
+            memoryerror("MAX_OBJ_PROP_TABLE_SIZE",MAX_OBJ_PROP_TABLE_SIZE);
+          }
+
+          for (i=0; i<prop_length; i++) {
+            int ppos = full_object_g.propdatasize++;
+            full_object_g.propdata[ppos].value = prop_addr + 4*i;
+            full_object_g.propdata[ppos].marker = INHERIT_MV;
+            full_object_g.propdata[ppos].type = CONSTANT_OT;
+          }
+        }
+        else {
+          /*  The ordinary case: the full_object_g property
+              values simply overrides the class definition,
+              so we skip over the values in the class table. */
+        }
+      }
+          else {
+            /*  The case where the class defined a property which wasn't
+                defined at all in full_object_g: we copy out the data into
+                a new property added to full_object_g. */
+            k = full_object_g.numprops++;
+            full_object_g.props[k].num = prop_number;
+            full_object_g.props[k].flags = prop_flags;
+            full_object_g.props[k].datastart = full_object_g.propdatasize;
+            full_object_g.props[k].continuation = 0;
+            full_object_g.props[k].datalen = prop_length;
+            if (full_object_g.propdatasize + prop_length 
+              > MAX_OBJ_PROP_TABLE_SIZE) {
+              memoryerror("MAX_OBJ_PROP_TABLE_SIZE",MAX_OBJ_PROP_TABLE_SIZE);
+            }
+
+            for (i=0; i<prop_length; i++) {
+              int ppos = full_object_g.propdatasize++;
+              full_object_g.propdata[ppos].value = prop_addr + 4*i;
+              full_object_g.propdata[ppos].marker = INHERIT_MV; 
+              full_object_g.propdata[ppos].type = CONSTANT_OT;
+            }
+          }
+
+          if (full_object_g.numprops == MAX_OBJ_PROP_COUNT) {
+            memoryerror("MAX_OBJ_PROP_COUNT",MAX_OBJ_PROP_COUNT);
+          }
+    }
+  }
+  
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Construction of Z-machine-format property blocks.                       */
+/* ------------------------------------------------------------------------- */
+
+static int write_properties_between(uchar *p, int mark, int from, int to)
+{   int j, k, prop_number, prop_length;
+    /* Note that p is properties_table. */
+    for (prop_number=to; prop_number>=from; prop_number--)
+    {   for (j=0; j<full_object.l; j++)
+        {   if ((full_object.pp[j].num == prop_number)
+                && (full_object.pp[j].l != 100))
+            {   prop_length = 2*full_object.pp[j].l;
+                if (mark+2+prop_length >= MAX_PROP_TABLE_SIZE)
+                    memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
+                if (version_number == 3)
+                    p[mark++] = prop_number + (prop_length - 1)*32;
+                else
+                {   switch(prop_length)
+                    {   case 1:
+                          p[mark++] = prop_number; break;
+                        case 2:
+                          p[mark++] = prop_number + 0x40; break;
+                        default:
+                          p[mark++] = prop_number + 0x80;
+                          p[mark++] = prop_length + 0x80; break;
+                    }
+                }
+
+                for (k=0; k<full_object.pp[j].l; k++)
+                {   if (full_object.pp[j].ao[k].marker != 0)
+                        backpatch_zmachine(full_object.pp[j].ao[k].marker,
+                            PROP_ZA, mark);
+                    p[mark++] = full_object.pp[j].ao[k].value/256;
+                    p[mark++] = full_object.pp[j].ao[k].value%256;
+                }
+            }
+        }
+    }
+
+    p[mark++]=0;
+    return(mark);
+}
+
+static int write_property_block_z(char *shortname)
+{
+    /*  Compile the (now complete) full_object properties into a
+        property-table block at "p" in Inform's memory.
+        "shortname" is the object's short name, if specified; otherwise
+        NULL.
+
+        Return the number of bytes written to the block.                     */
+
+    int32 mark = properties_table_size, i;
+    uchar *p = (uchar *) properties_table;
+
+    /* printf("Object at %04x\n", mark); */
+
+    if (shortname != NULL)
+    {   uchar *tmp;
+        if (mark+1+510 >= MAX_PROP_TABLE_SIZE)
+            memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
+        tmp = translate_text(p+mark+1,p+mark+1+510,shortname);
+        if (!tmp) error ("Short name of object exceeded 765 Z-characters");
+        i = subtract_pointers(tmp,(p+mark+1));
+        p[mark] = i/2;
+        mark += i+1;
+    }
+    if (current_defn_is_class)
+    {   mark = write_properties_between(p,mark,3,3);
+        for (i=0;i<6;i++)
+            p[mark++] = full_object.atts[i];
+        class_begins_at[no_classes++] = mark;
+    }
+
+    mark = write_properties_between(p, mark, 1, (version_number==3)?31:63);
+
+    i = mark - properties_table_size;
+    properties_table_size = mark;
+
+    return(i);
+}
+
+static int gpropsort(void *ptr1, void *ptr2)
+{
+  propg *prop1 = ptr1;
+  propg *prop2 = ptr2;
+  
+  if (prop2->num == -1)
+    return -1;
+  if (prop1->num == -1)
+    return 1;
+  if (prop1->num < prop2->num)
+    return -1;
+  if (prop1->num > prop2->num)
+    return 1;
+
+  return (prop1->continuation - prop2->continuation);
+}
+
+static int32 write_property_block_g(void)
+{
+  /*  Compile the (now complete) full_object properties into a
+      property-table block at "p" in Inform's memory. 
+      Return the number of bytes written to the block. 
+      In Glulx, the shortname property isn't used here; it's already
+      been compiled into an ordinary string. */
+
+  int32 i;
+  int ix, jx, kx, totalprops;
+  int32 mark = properties_table_size;
+  int32 datamark;
+  uchar *p = (uchar *) properties_table;
+
+  if (current_defn_is_class) {
+    for (i=0;i<NUM_ATTR_BYTES;i++)
+      p[mark++] = full_object_g.atts[i];
+    class_begins_at[no_classes++] = mark;
+  }
+
+  qsort(full_object_g.props, full_object_g.numprops, sizeof(propg), 
+    (int (*)(const void *, const void *))(&gpropsort));
+
+  full_object_g.finalpropaddr = mark;
+
+  totalprops = 0;
+
+  for (ix=0; ix<full_object_g.numprops; ix=jx) {
+    int propnum = full_object_g.props[ix].num;
+    if (propnum == -1)
+        break;
+    for (jx=ix; 
+        jx<full_object_g.numprops && full_object_g.props[jx].num == propnum;
+        jx++);
+    totalprops++;
+  }
+
+  /* Write out the number of properties in this table. */
+  if (mark+4 >= MAX_PROP_TABLE_SIZE)
+      memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
+  WriteInt32(p+mark, totalprops);
+  mark += 4;
+
+  datamark = mark + 10*totalprops;
+
+  for (ix=0; ix<full_object_g.numprops; ix=jx) {
+    int propnum = full_object_g.props[ix].num;
+    int flags = full_object_g.props[ix].flags;
+    int totallen = 0;
+    int32 datamarkstart = datamark;
+    if (propnum == -1)
+      break;
+    for (jx=ix; 
+        jx<full_object_g.numprops && full_object_g.props[jx].num == propnum;
+        jx++) {
+      int32 datastart = full_object_g.props[jx].datastart;
+      if (datamark+4*full_object_g.props[jx].datalen >= MAX_PROP_TABLE_SIZE)
+        memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
+      for (kx=0; kx<full_object_g.props[jx].datalen; kx++) {
+        int32 val = full_object_g.propdata[datastart+kx].value;
+        WriteInt32(p+datamark, val);
+        if (full_object_g.propdata[datastart+kx].marker != 0)
+          backpatch_zmachine(full_object_g.propdata[datastart+kx].marker,
+            PROP_ZA, datamark);
+        totallen++;
+        datamark += 4;
+      }
+    }
+    if (mark+10 >= MAX_PROP_TABLE_SIZE)
+        memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
+    WriteInt16(p+mark, propnum);
+    mark += 2;
+    WriteInt16(p+mark, totallen);
+    mark += 2;
+    WriteInt32(p+mark, datamarkstart); 
+    mark += 4;
+    WriteInt16(p+mark, flags);
+    mark += 2;
+  }
+
+  mark = datamark;
+
+  i = mark - properties_table_size;
+  properties_table_size = mark;
+  return i;
+}
+
+/* ------------------------------------------------------------------------- */
+/*   The final stage in Nearby/Object/Class definition processing.           */
+/* ------------------------------------------------------------------------- */
+
+static void manufacture_object_z(void)
+{   int i, j;
+
+    segment_markers.enabled = FALSE;
+    directives.enabled = TRUE;
+
+    property_inheritance_z();
+
+    objectsz[no_objects].parent = parent_of_this_obj;
+    objectsz[no_objects].next = 0;
+    objectsz[no_objects].child = 0;
+
+    if ((parent_of_this_obj > 0) && (parent_of_this_obj != 0x7fff))
+    {   i = objectsz[parent_of_this_obj-1].child;
+        if (i == 0)
+            objectsz[parent_of_this_obj-1].child = no_objects + 1;
+        else
+        {   while(objectsz[i-1].next != 0) i = objectsz[i-1].next;
+            objectsz[i-1].next = no_objects+1;
+        }
+    }
+
+        /*  The properties table consists simply of a sequence of property
+            blocks, one for each object in order of definition, exactly as
+            it will appear in the final Z-machine.                           */
+
+    j = write_property_block_z(shortname_buffer);
+
+    objectsz[no_objects].propsize = j;
+    if (properties_table_size >= MAX_PROP_TABLE_SIZE)
+        memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
+
+    if (current_defn_is_class)
+        for (i=0;i<6;i++) objectsz[no_objects].atts[i] = 0;
+    else
+        for (i=0;i<6;i++)
+            objectsz[no_objects].atts[i] = full_object.atts[i];
+
+    no_objects++;
+}
+
+static void manufacture_object_g(void)
+{   int32 i, j;
+
+    segment_markers.enabled = FALSE;
+    directives.enabled = TRUE;
+
+    property_inheritance_g();
+
+    objectsg[no_objects].parent = parent_of_this_obj;
+    objectsg[no_objects].next = 0;
+    objectsg[no_objects].child = 0;
+
+    if ((parent_of_this_obj > 0) && (parent_of_this_obj != 0x7fffffff))
+    {   i = objectsg[parent_of_this_obj-1].child;
+        if (i == 0)
+            objectsg[parent_of_this_obj-1].child = no_objects + 1;
+        else
+        {   while(objectsg[i-1].next != 0) i = objectsg[i-1].next;
+            objectsg[i-1].next = no_objects+1;
+        }
+    }
+
+    objectsg[no_objects].shortname = compile_string(shortname_buffer,
+      FALSE, FALSE);
+
+        /*  The properties table consists simply of a sequence of property
+            blocks, one for each object in order of definition, exactly as
+            it will appear in the final machine image.                      */
+
+    j = write_property_block_g();
+
+    objectsg[no_objects].propaddr = full_object_g.finalpropaddr;
+
+    objectsg[no_objects].propsize = j;
+    if (properties_table_size >= MAX_PROP_TABLE_SIZE)
+        memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
+
+    if (current_defn_is_class)
+        for (i=0;i<NUM_ATTR_BYTES;i++) 
+            objectatts[no_objects*NUM_ATTR_BYTES+i] = 0;
+    else
+        for (i=0;i<NUM_ATTR_BYTES;i++)
+            objectatts[no_objects*NUM_ATTR_BYTES+i] = full_object_g.atts[i];
+
+    no_objects++;
+}
+
+
+/* ========================================================================= */
+/*   [2]  The Object/Nearby/Class directives parser: translating the syntax  */
+/*        into object specifications and then triggering off the above.      */
+/* ========================================================================= */
+/*   Properties ("with" or "private") segment.                               */
+/* ------------------------------------------------------------------------- */
+
+static int *defined_this_segment;
+static long defined_this_segment_size; /* calloc size */
+static int def_t_s;
+
+static void ensure_defined_this_segment(int newsize)
+{
+    int oldsize = defined_this_segment_size;
+    defined_this_segment_size = newsize;
+    my_recalloc(&defined_this_segment, sizeof(int), oldsize,
+        defined_this_segment_size, "defined this segment table");
+}
+
+static void properties_segment_z(int this_segment)
+{
+    /*  Parse through the "with" part of an object/class definition:
+
+        <prop-1> <values...>, <prop-2> <values...>, ..., <prop-n> <values...>
+
+        This routine also handles "private", with this_segment being equal
+        to the token value for the introductory word ("private" or "with").  */
+
+
+    int   i, property_name_symbol, property_number=0, next_prop=0, length,
+          individual_property, this_identifier_number;
+
+    do
+    {   get_next_token_with_directives();
+        if ((token_type == SEGMENT_MARKER_TT)
+            || (token_type == EOF_TT)
+            || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
+        {   put_token_back(); return;
+        }
+
+        if (token_type != SYMBOL_TT)
+        {   ebf_error("property name", token_text);
+            return;
+        }
+
+        individual_property = (stypes[token_value] != PROPERTY_T);
+
+        if (individual_property)
+        {   if (sflags[token_value] & UNKNOWN_SFLAG)
+            {   this_identifier_number = no_individual_properties++;
+                assign_symbol(token_value, this_identifier_number,
+                    INDIVIDUAL_PROPERTY_T);
+
+                if (debugfile_switch)
+                {   debug_file_printf("<property>");
+                    debug_file_printf
+                        ("<identifier>%s</identifier>", token_text);
+                    debug_file_printf
+                        ("<value>%d</value>", this_identifier_number);
+                    debug_file_printf("</property>");
+                }
+
+            }
+            else
+            {   if (stypes[token_value]==INDIVIDUAL_PROPERTY_T)
+                    this_identifier_number = svals[token_value];
+                else
+                {   char already_error[128];
+                    sprintf(already_error,
+                        "\"%s\" is a name already in use (with type %s) \
+and may not be used as a property name too",
+                        token_text, typename(stypes[token_value]));
+                    error(already_error);
+                    return;
+                }
+            }
+
+            if (def_t_s >= defined_this_segment_size)
+                ensure_defined_this_segment(def_t_s*2);
+            defined_this_segment[def_t_s++] = token_value;
+
+            if (individual_prop_table_size++ == 0)
+            {   full_object.pp[full_object.l].num = 3;
+                full_object.pp[full_object.l].l = 1;
+                full_object.pp[full_object.l].ao[0].value
+                    = individuals_length;
+                full_object.pp[full_object.l].ao[0].type = LONG_CONSTANT_OT;
+                full_object.pp[full_object.l].ao[0].marker = INDIVPT_MV;
+
+                i_m = individuals_length;
+                full_object.l++;
+            }
+            individuals_table[i_m] = this_identifier_number/256;
+            if (this_segment == PRIVATE_SEGMENT)
+                individuals_table[i_m] |= 0x80;
+            individuals_table[i_m+1] = this_identifier_number%256;
+            if (module_switch)
+                backpatch_zmachine(IDENT_MV, INDIVIDUAL_PROP_ZA, i_m);
+            individuals_table[i_m+2] = 0;
+        }
+        else
+        {   if (sflags[token_value] & UNKNOWN_SFLAG)
+            {   error_named("No such property name as", token_text);
+                return;
+            }
+            if (this_segment == PRIVATE_SEGMENT)
+                error_named("Property should be declared in 'with', \
+not 'private':", token_text);
+            if (def_t_s >= defined_this_segment_size)
+                ensure_defined_this_segment(def_t_s*2);
+            defined_this_segment[def_t_s++] = token_value;
+            property_number = svals[token_value];
+
+            next_prop=full_object.l++;
+            full_object.pp[next_prop].num = property_number;
+        }
+
+        for (i=0; i<(def_t_s-1); i++)
+            if (defined_this_segment[i] == token_value)
+            {   error_named("Property given twice in the same declaration:",
+                    (char *) symbs[token_value]);
+            }
+            else
+            if (svals[defined_this_segment[i]] == svals[token_value])
+            {   char error_b[128];
+                sprintf(error_b,
+                    "Property given twice in the same declaration, because \
+the names '%s' and '%s' actually refer to the same property",
+                    (char *) symbs[defined_this_segment[i]],
+                    (char *) symbs[token_value]);
+                error(error_b);
+            }
+
+        property_name_symbol = token_value;
+        sflags[token_value] |= USED_SFLAG;
+
+        length=0;
+        do
+        {   assembly_operand AO;
+            get_next_token_with_directives();
+            if ((token_type == EOF_TT)
+                || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
+                || ((token_type == SEP_TT) && (token_value == COMMA_SEP)))
+                break;
+
+            if (token_type == SEGMENT_MARKER_TT) { put_token_back(); break; }
+
+            if ((!individual_property) && (property_number==1)
+                && ((token_type != SQ_TT) || (strlen(token_text) <2 )) 
+                && (token_type != DQ_TT)
+                )
+                warning ("'name' property should only contain dictionary words");
+
+            if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
+            {   char embedded_name[80];
+                if (current_defn_is_class)
+                {   sprintf(embedded_name,
+                        "%s::%s", classname_text,
+                        (char *) symbs[property_name_symbol]);
+                }
+                else
+                {   sprintf(embedded_name,
+                        "%s.%s", objectname_text,
+                        (char *) symbs[property_name_symbol]);
+                }
+                AO.value = parse_routine(NULL, TRUE, embedded_name, FALSE, -1);
+                AO.type = LONG_CONSTANT_OT;
+                AO.marker = IROUTINE_MV;
+
+                directives.enabled = FALSE;
+                segment_markers.enabled = TRUE;
+
+                statements.enabled = FALSE;
+                misc_keywords.enabled = FALSE;
+                local_variables.enabled = FALSE;
+                system_functions.enabled = FALSE;
+                conditions.enabled = FALSE;
+            }
+            else
+
+            /*  A special rule applies to values in double-quotes of the
+                built-in property "name", which always has number 1: such
+                property values are dictionary entries and not static
+                strings                                                      */
+
+            if ((!individual_property) &&
+                (property_number==1) && (token_type == DQ_TT))
+            {   AO.value = dictionary_add(token_text, 0x80, 0, 0);
+                AO.type = LONG_CONSTANT_OT;
+                AO.marker = DWORD_MV;
+            }
+            else
+            {   if (length!=0)
+                {
+                    if ((token_type == SYMBOL_TT)
+                        && (stypes[token_value]==PROPERTY_T))
+                    {
+                        /*  This is not necessarily an error: it's possible
+                            to imagine a property whose value is a list
+                            of other properties to look up, but far more
+                            likely that a comma has been omitted in between
+                            two property blocks                              */
+
+                        warning_named(
+               "Missing ','? Property data seems to contain the property name",
+                            token_text);
+                    }
+                }
+
+                /*  An ordinary value, then:                                 */
+
+                put_token_back();
+                AO = parse_expression(ARRAY_CONTEXT);
+            }
+
+            if (length == 64)
+            {   error_named("Limit (of 32 values) exceeded for property",
+                    (char *) symbs[property_name_symbol]);
+                break;
+            }
+
+            if (individual_property)
+            {   if (AO.marker != 0)
+                    backpatch_zmachine(AO.marker, INDIVIDUAL_PROP_ZA,
+                        i_m+3+length);
+                individuals_table[i_m+3+length++] = AO.value/256;
+                individuals_table[i_m+3+length++] = AO.value%256;
+            }
+            else
+            {   full_object.pp[next_prop].ao[length/2] = AO;
+                length = length + 2;
+            }
+
+        } while (TRUE);
+
+        /*  People rarely do, but it is legal to declare a property without
+            a value at all:
+
+                with  name "fish", number, time_left;
+
+            in which case the properties "number" and "time_left" are
+            created as in effect variables and initialised to zero.          */
+
+        if (length == 0)
+        {   if (individual_property)
+            {   individuals_table[i_m+3+length++] = 0;
+                individuals_table[i_m+3+length++] = 0;
+            }
+            else
+            {   full_object.pp[next_prop].ao[0].value = 0;
+                full_object.pp[next_prop].ao[0].type  = LONG_CONSTANT_OT;
+                full_object.pp[next_prop].ao[0].marker = 0;
+                length = 2;
+            }
+        }
+
+        if ((version_number==3) && (!individual_property))
+        {   if (length > 8)
+            {
+       warning_named("Version 3 limit of 4 values per property exceeded \
+(use -v5 to get 32), so truncating property",
+                    (char *) symbs[property_name_symbol]);
+                full_object.pp[next_prop].l=4;
+            }
+        }
+
+        if (individual_property)
+        {
+            if (individuals_length+length+3 > MAX_INDIV_PROP_TABLE_SIZE)
+                memoryerror("MAX_INDIV_PROP_TABLE_SIZE",
+                    MAX_INDIV_PROP_TABLE_SIZE);
+            individuals_table[i_m + 2] = length;
+            individuals_length += length+3;
+            i_m = individuals_length;
+        }
+        else
+            full_object.pp[next_prop].l = length/2;
+
+        if ((token_type == EOF_TT)
+            || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
+        {   put_token_back(); return;
+        }
+
+    } while (TRUE);
+}
+
+
+static void properties_segment_g(int this_segment)
+{
+    /*  Parse through the "with" part of an object/class definition:
+
+        <prop-1> <values...>, <prop-2> <values...>, ..., <prop-n> <values...>
+
+        This routine also handles "private", with this_segment being equal
+        to the token value for the introductory word ("private" or "with").  */
+
+
+    int   i, next_prop,
+          individual_property, this_identifier_number;
+    int32 property_name_symbol, property_number, length;
+
+    do
+    {   get_next_token_with_directives();
+        if ((token_type == SEGMENT_MARKER_TT)
+            || (token_type == EOF_TT)
+            || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
+        {   put_token_back(); return;
+        }
+
+        if (token_type != SYMBOL_TT)
+        {   ebf_error("property name", token_text);
+            return;
+        }
+
+        individual_property = (stypes[token_value] != PROPERTY_T);
+
+        if (individual_property)
+        {   if (sflags[token_value] & UNKNOWN_SFLAG)
+            {   this_identifier_number = no_individual_properties++;
+                assign_symbol(token_value, this_identifier_number,
+                    INDIVIDUAL_PROPERTY_T);
+
+                if (debugfile_switch)
+                {   debug_file_printf("<property>");
+                    debug_file_printf
+                        ("<identifier>%s</identifier>", token_text);
+                    debug_file_printf
+                        ("<value>%d</value>", this_identifier_number);
+                    debug_file_printf("</property>");
+                }
+
+            }
+            else
+            {   if (stypes[token_value]==INDIVIDUAL_PROPERTY_T)
+                    this_identifier_number = svals[token_value];
+                else
+                {   char already_error[128];
+                    sprintf(already_error,
+                        "\"%s\" is a name already in use (with type %s) \
+and may not be used as a property name too",
+                        token_text, typename(stypes[token_value]));
+                    error(already_error);
+                    return;
+                }
+            }
+
+            if (def_t_s >= defined_this_segment_size)
+                ensure_defined_this_segment(def_t_s*2);
+            defined_this_segment[def_t_s++] = token_value;
+            property_number = svals[token_value];
+
+            next_prop=full_object_g.numprops++;
+            full_object_g.props[next_prop].num = property_number;
+            full_object_g.props[next_prop].flags = 
+              ((this_segment == PRIVATE_SEGMENT) ? 1 : 0);
+            full_object_g.props[next_prop].datastart = full_object_g.propdatasize;
+            full_object_g.props[next_prop].continuation = 0;
+            full_object_g.props[next_prop].datalen = 0;
+        }
+        else
+        {   if (sflags[token_value] & UNKNOWN_SFLAG)
+            {   error_named("No such property name as", token_text);
+                return;
+            }
+            if (this_segment == PRIVATE_SEGMENT)
+                error_named("Property should be declared in 'with', \
+not 'private':", token_text);
+
+            if (def_t_s >= defined_this_segment_size)
+                ensure_defined_this_segment(def_t_s*2);
+            defined_this_segment[def_t_s++] = token_value;
+            property_number = svals[token_value];
+
+            next_prop=full_object_g.numprops++;
+            full_object_g.props[next_prop].num = property_number;
+            full_object_g.props[next_prop].flags = 0;
+            full_object_g.props[next_prop].datastart = full_object_g.propdatasize;
+            full_object_g.props[next_prop].continuation = 0;
+            full_object_g.props[next_prop].datalen = 0;
+        }
+
+        for (i=0; i<(def_t_s-1); i++)
+            if (defined_this_segment[i] == token_value)
+            {   error_named("Property given twice in the same declaration:",
+                    (char *) symbs[token_value]);
+            }
+            else
+            if (svals[defined_this_segment[i]] == svals[token_value])
+            {   char error_b[128];
+                sprintf(error_b,
+                    "Property given twice in the same declaration, because \
+the names '%s' and '%s' actually refer to the same property",
+                    (char *) symbs[defined_this_segment[i]],
+                    (char *) symbs[token_value]);
+                error(error_b);
+            }
+
+        if (full_object_g.numprops == MAX_OBJ_PROP_COUNT) {
+          memoryerror("MAX_OBJ_PROP_COUNT",MAX_OBJ_PROP_COUNT);
+        }
+
+        property_name_symbol = token_value;
+        sflags[token_value] |= USED_SFLAG;
+
+        length=0;
+        do
+        {   assembly_operand AO;
+            get_next_token_with_directives();
+            if ((token_type == EOF_TT)
+                || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
+                || ((token_type == SEP_TT) && (token_value == COMMA_SEP)))
+                break;
+
+            if (token_type == SEGMENT_MARKER_TT) { put_token_back(); break; }
+
+            if ((!individual_property) && (property_number==1)
+                && (token_type != SQ_TT) && (token_type != DQ_TT)
+                )
+                warning ("'name' property should only contain dictionary words");
+
+            if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
+            {   char embedded_name[80];
+                if (current_defn_is_class)
+                {   sprintf(embedded_name,
+                        "%s::%s", classname_text,
+                        (char *) symbs[property_name_symbol]);
+                }
+                else
+                {   sprintf(embedded_name,
+                        "%s.%s", objectname_text,
+                        (char *) symbs[property_name_symbol]);
+                }
+                AO.value = parse_routine(NULL, TRUE, embedded_name, FALSE, -1);
+                AO.type = CONSTANT_OT; 
+                AO.marker = IROUTINE_MV;
+
+                directives.enabled = FALSE;
+                segment_markers.enabled = TRUE;
+
+                statements.enabled = FALSE;
+                misc_keywords.enabled = FALSE;
+                local_variables.enabled = FALSE;
+                system_functions.enabled = FALSE;
+                conditions.enabled = FALSE;
+            }
+            else
+
+            /*  A special rule applies to values in double-quotes of the
+                built-in property "name", which always has number 1: such
+                property values are dictionary entries and not static
+                strings                                                      */
+
+            if ((!individual_property) &&
+                (property_number==1) && (token_type == DQ_TT))
+            {   AO.value = dictionary_add(token_text, 0x80, 0, 0);
+                AO.type = CONSTANT_OT; 
+                AO.marker = DWORD_MV;
+            }
+            else
+            {   if (length!=0)
+                {
+                    if ((token_type == SYMBOL_TT)
+                        && (stypes[token_value]==PROPERTY_T))
+                    {
+                        /*  This is not necessarily an error: it's possible
+                            to imagine a property whose value is a list
+                            of other properties to look up, but far more
+                            likely that a comma has been omitted in between
+                            two property blocks                              */
+
+                        warning_named(
+               "Missing ','? Property data seems to contain the property name",
+                            token_text);
+                    }
+                }
+
+                /*  An ordinary value, then:                                 */
+
+                put_token_back();
+                AO = parse_expression(ARRAY_CONTEXT);
+            }
+
+            if (length == 32768) /* VENEER_CONSTRAINT_ON_PROP_TABLE_SIZE? */
+            {   error_named("Limit (of 32768 values) exceeded for property",
+                    (char *) symbs[property_name_symbol]);
+                break;
+            }
+
+            if (full_object_g.propdatasize >= MAX_OBJ_PROP_TABLE_SIZE) {
+              memoryerror("MAX_OBJ_PROP_TABLE_SIZE",MAX_OBJ_PROP_TABLE_SIZE);
+            }
+
+            full_object_g.propdata[full_object_g.propdatasize++] = AO;
+            length += 1;
+
+        } while (TRUE);
+
+        /*  People rarely do, but it is legal to declare a property without
+            a value at all:
+
+                with  name "fish", number, time_left;
+
+            in which case the properties "number" and "time_left" are
+            created as in effect variables and initialised to zero.          */
+
+        if (length == 0)
+        {
+            assembly_operand AO;
+            AO.value = 0;
+            AO.type = CONSTANT_OT;
+            AO.marker = 0;
+            full_object_g.propdata[full_object_g.propdatasize++] = AO;
+            length += 1;
+        }
+
+        full_object_g.props[next_prop].datalen = length;
+
+        if ((token_type == EOF_TT)
+            || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
+        {   put_token_back(); return;
+        }
+
+    } while (TRUE);
+}
+
+static void properties_segment(int this_segment)
+{
+  if (!glulx_mode)
+    properties_segment_z(this_segment);
+  else
+    properties_segment_g(this_segment);
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Attributes ("has") segment.                                             */
+/* ------------------------------------------------------------------------- */
+
+static void attributes_segment(void)
+{
+    /*  Parse through the "has" part of an object/class definition:
+
+        [~]<attribute-1> [~]<attribute-2> ... [~]<attribute-n>               */
+
+    int attribute_number, truth_state, bitmask;
+    uchar *attrbyte;
+    do
+    {   truth_state = TRUE;
+
+        ParseAttrN:
+
+        get_next_token_with_directives();
+        if ((token_type == SEGMENT_MARKER_TT)
+            || (token_type == EOF_TT)
+            || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
+        {   if (!truth_state)
+                ebf_error("attribute name after '~'", token_text);
+            put_token_back(); return;
+        }
+        if ((token_type == SEP_TT) && (token_value == COMMA_SEP)) return;
+
+        if ((token_type == SEP_TT) && (token_value == ARTNOT_SEP))
+        {   truth_state = !truth_state; goto ParseAttrN;
+        }
+
+        if ((token_type != SYMBOL_TT)
+            || (stypes[token_value] != ATTRIBUTE_T))
+        {   ebf_error("name of an already-declared attribute", token_text);
+            return;
+        }
+
+        attribute_number = svals[token_value];
+        sflags[token_value] |= USED_SFLAG;
+
+        if (!glulx_mode) {
+            bitmask = (1 << (7-attribute_number%8));
+            attrbyte = &(full_object.atts[attribute_number/8]);
+        }
+        else {
+            /* In Glulx, my prejudices rule, and therefore bits are numbered
+               from least to most significant. This is the opposite of the
+               way the Z-machine works. */
+            bitmask = (1 << (attribute_number%8));
+            attrbyte = &(full_object_g.atts[attribute_number/8]);
+        }
+
+        if (truth_state)
+            *attrbyte |= bitmask;     /* Set attribute bit */
+        else
+            *attrbyte &= ~bitmask;    /* Clear attribute bit */
+
+    } while (TRUE);
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Classes ("class") segment.                                              */
+/* ------------------------------------------------------------------------- */
+
+static void add_class_to_inheritance_list(int class_number)
+{
+    int i;
+
+    /*  The class number is actually the class's object number, which needs
+        to be translated into its actual class number:                       */
+
+    for (i=0;i<no_classes;i++)
+        if (class_number == class_object_numbers[i])
+        {   class_number = i+1;
+            break;
+        }
+
+    /*  Remember the inheritance list so that property inheritance can
+        be sorted out later on, when the definition has been finished:       */
+
+    classes_to_inherit_from[no_classes_to_inherit_from++] = class_number;
+
+    /*  Inheriting attributes from the class at once:                        */
+
+    if (!glulx_mode) {
+        for (i=0; i<6; i++)
+            full_object.atts[i]
+                |= properties_table[class_begins_at[class_number-1] - 6 + i];
+    }
+    else {
+        for (i=0; i<NUM_ATTR_BYTES; i++)
+            full_object_g.atts[i]
+                |= properties_table[class_begins_at[class_number-1] 
+                    - NUM_ATTR_BYTES + i];
+    }
+}
+
+static void classes_segment(void)
+{
+    /*  Parse through the "class" part of an object/class definition:
+
+        <class-1> ... <class-n>                                              */
+
+    do
+    {   get_next_token_with_directives();
+        if ((token_type == SEGMENT_MARKER_TT)
+            || (token_type == EOF_TT)
+            || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
+        {   put_token_back(); return;
+        }
+        if ((token_type == SEP_TT) && (token_value == COMMA_SEP)) return;
+
+        if ((token_type != SYMBOL_TT)
+            || (stypes[token_value] != CLASS_T))
+        {   ebf_error("name of an already-declared class", token_text);
+            return;
+        }
+
+        sflags[token_value] |= USED_SFLAG;
+        add_class_to_inheritance_list(svals[token_value]);
+    } while (TRUE);
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Parse the body of a Nearby/Object/Class definition.                     */
+/* ------------------------------------------------------------------------- */
+
+static void parse_body_of_definition(void)
+{   int commas_in_row;
+
+    def_t_s = 0;
+
+    do
+    {   commas_in_row = -1;
+        do
+        {   get_next_token_with_directives(); commas_in_row++;
+        } while ((token_type == SEP_TT) && (token_value == COMMA_SEP));
+
+        if (commas_in_row>1)
+            error("Two commas ',' in a row in object/class definition");
+
+        if ((token_type == EOF_TT)
+            || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
+        {   if (commas_in_row > 0)
+                error("Object/class definition finishes with ','");
+            if (token_type == EOF_TT)
+                error("Object/class definition incomplete (no ';') at end of file");
+            break;
+        }
+
+        if (token_type != SEGMENT_MARKER_TT)
+        {   error_named("Expected 'with', 'has' or 'class' in \
+object/class definition but found", token_text);
+            break;
+        }
+        else
+        switch(token_value)
+        {   case WITH_SEGMENT:
+                properties_segment(WITH_SEGMENT);
+                break;
+            case PRIVATE_SEGMENT:
+                properties_segment(PRIVATE_SEGMENT);
+                break;
+            case HAS_SEGMENT:
+                attributes_segment();
+                break;
+            case CLASS_SEGMENT:
+                classes_segment();
+                break;
+        }
+
+    } while (TRUE);
+
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Class directives:                                                       */
+/*                                                                           */
+/*        Class <name>  <body of definition>                                 */
+/* ------------------------------------------------------------------------- */
+
+static void initialise_full_object(void)
+{
+  int i;
+  if (!glulx_mode) {
+    full_object.l = 0;
+    full_object.atts[0] = 0;
+    full_object.atts[1] = 0;
+    full_object.atts[2] = 0;
+    full_object.atts[3] = 0;
+    full_object.atts[4] = 0;
+    full_object.atts[5] = 0;
+  }
+  else {
+    full_object_g.numprops = 0;
+    full_object_g.propdatasize = 0;
+    for (i=0; i<NUM_ATTR_BYTES; i++)
+      full_object_g.atts[i] = 0;
+  }
+}
+
+extern void make_class(char * metaclass_name)
+{   int n, duplicates_to_make = 0, class_number = no_objects+1,
+        metaclass_flag = (metaclass_name != NULL);
+    char duplicate_name[128];
+    int class_symbol;
+    debug_location_beginning beginning_debug_location =
+        get_token_location_beginning();
+
+    current_defn_is_class = TRUE; no_classes_to_inherit_from = 0;
+    individual_prop_table_size = 0;
+
+    if (no_classes==MAX_CLASSES)
+        memoryerror("MAX_CLASSES", MAX_CLASSES);
+
+    if (no_classes==VENEER_CONSTRAINT_ON_CLASSES)
+        fatalerror("Inform's maximum possible number of classes (whatever \
+amount of memory is allocated) has been reached. If this causes serious \
+inconvenience, please contact the maintainers.");
+
+    directives.enabled = FALSE;
+
+    if (metaclass_flag)
+    {   token_text = metaclass_name;
+        token_value = symbol_index(token_text, -1);
+        token_type = SYMBOL_TT;
+    }
+    else
+    {   get_next_token();
+        if ((token_type != SYMBOL_TT)
+            || (!(sflags[token_value] & UNKNOWN_SFLAG)))
+        {   discard_token_location(beginning_debug_location);
+            ebf_error("new class name", token_text);
+            panic_mode_error_recovery();
+            return;
+        }
+    }
+
+    /*  Each class also creates a modest object representing itself:         */
+
+    strcpy(shortname_buffer, token_text);
+
+    assign_symbol(token_value, class_number, CLASS_T);
+    classname_text = (char *) symbs[token_value];
+
+    if (!glulx_mode) {
+        if (metaclass_flag) sflags[token_value] |= SYSTEM_SFLAG;
+    }
+    else {
+        /*  In Glulx, metaclasses have to be backpatched too! So we can't 
+            mark it as "system", but we should mark it "used". */
+        if (metaclass_flag) sflags[token_value] |= USED_SFLAG;
+    }
+
+    /*  "Class" (object 1) has no parent, whereas all other classes are
+        the children of "Class".  Since "Class" is not present in a module,
+        a special value is used which is corrected to 1 by the linker.       */
+
+    if (metaclass_flag) parent_of_this_obj = 0;
+    else parent_of_this_obj = (module_switch)?MAXINTWORD:1;
+
+    class_object_numbers[no_classes] = class_number;
+
+    initialise_full_object();
+
+    /*  Give the class the (nameless in Inform syntax) "inheritance" property
+        with value its own class number.  (This therefore accumulates onto
+        the inheritance property of any object inheriting from the class,
+        since property 2 is always set to "additive" -- see below)           */
+
+    if (!glulx_mode) {
+      full_object.l = 1;
+      full_object.pp[0].num = 2;
+      full_object.pp[0].l = 1;
+      full_object.pp[0].ao[0].value  = no_objects + 1;
+      full_object.pp[0].ao[0].type   = LONG_CONSTANT_OT;
+      full_object.pp[0].ao[0].marker = OBJECT_MV;
+    }
+    else {
+      full_object_g.numprops = 1;
+      full_object_g.props[0].num = 2;
+      full_object_g.props[0].flags = 0;
+      full_object_g.props[0].datastart = 0;
+      full_object_g.props[0].continuation = 0;
+      full_object_g.props[0].datalen = 1;
+      full_object_g.propdatasize = 1;
+      full_object_g.propdata[0].value  = no_objects + 1;
+      full_object_g.propdata[0].type   = CONSTANT_OT;
+      full_object_g.propdata[0].marker = OBJECT_MV;
+    }
+
+    class_symbol = token_value;
+
+    if (!metaclass_flag)
+    {   get_next_token();
+        if ((token_type == SEP_TT) && (token_value == OPENB_SEP))
+        {   assembly_operand AO;
+            AO = parse_expression(CONSTANT_CONTEXT);
+            if (AO.marker != 0)
+            {   error("Duplicate-number not known at compile time");
+                n=0;
+            }
+            else
+                n = AO.value;
+            if ((n<0) || (n>10000))
+            {   error("The number of duplicates must be 0 to 10000");
+                n=0;
+            }
+
+            /*  Make one extra duplicate, since the veneer routines need
+                always to keep an undamaged prototype for the class in stock */
+
+            duplicates_to_make = n + 1;
+
+            match_close_bracket();
+        } else put_token_back();
+
+        /*  Parse the body of the definition:                                */
+
+        parse_body_of_definition();
+    }
+
+    if (debugfile_switch)
+    {   debug_file_printf("<class>");
+        debug_file_printf("<identifier>%s</identifier>", shortname_buffer);
+        debug_file_printf("<class-number>%d</class-number>", no_classes);
+        debug_file_printf("<value>");
+        write_debug_object_backpatch(no_objects + 1);
+        debug_file_printf("</value>");
+        write_debug_locations
+            (get_token_location_end(beginning_debug_location));
+        debug_file_printf("</class>");
+    }
+
+    if (!glulx_mode)
+      manufacture_object_z();
+    else
+      manufacture_object_g();
+
+    if (individual_prop_table_size >= VENEER_CONSTRAINT_ON_IP_TABLE_SIZE)
+        error("This class is too complex: it now carries too many properties. \
+You may be able to get round this by declaring some of its property names as \
+\"common properties\" using the 'Property' directive.");
+
+    if (duplicates_to_make > 0)
+    {   sprintf(duplicate_name, "%s_1", shortname_buffer);
+        for (n=1; (duplicates_to_make--) > 0; n++)
+        {   if (n>1)
+            {   int i = strlen(duplicate_name);
+                while (duplicate_name[i] != '_') i--;
+                sprintf(duplicate_name+i+1, "%d", n);
+            }
+            make_object(FALSE, duplicate_name, class_number, class_number, -1);
+        }
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Object/Nearby directives:                                               */
+/*                                                                           */
+/*       Object  <name-1> ... <name-n> "short name"  [parent]  <body of def> */
+/*                                                                           */
+/*       Nearby  <name-1> ... <name-n> "short name"  <body of definition>    */
+/* ------------------------------------------------------------------------- */
+
+static int end_of_header(void)
+{   if (((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
+        || ((token_type == SEP_TT) && (token_value == COMMA_SEP))
+        || (token_type == SEGMENT_MARKER_TT)) return TRUE;
+    return FALSE;
+}
+
+extern void make_object(int nearby_flag,
+    char *textual_name, int specified_parent, int specified_class,
+    int instance_of)
+{
+    /*  Ordinarily this is called with nearby_flag TRUE for "Nearby",
+        FALSE for "Object"; and textual_name NULL, specified_parent and
+        specified_class both -1.  The next three arguments are used when
+        the routine is called for class duplicates manufacture (see above).
+        The last is used to create instances of a particular class.  */
+
+    int i, tree_depth, internal_name_symbol = 0;
+    char internal_name[64];
+    debug_location_beginning beginning_debug_location =
+        get_token_location_beginning();
+
+    directives.enabled = FALSE;
+
+    if (no_objects==MAX_OBJECTS) memoryerror("MAX_OBJECTS", MAX_OBJECTS);
+
+    sprintf(internal_name, "nameless_obj__%d", no_objects+1);
+    objectname_text = internal_name;
+
+    current_defn_is_class = FALSE;
+
+    no_classes_to_inherit_from=0;
+
+    individual_prop_table_size = 0;
+
+    if (nearby_flag) tree_depth=1; else tree_depth=0;
+
+    if (specified_class != -1) goto HeaderPassed;
+
+    get_next_token();
+
+    /*  Read past and count a sequence of "->"s, if any are present          */
+
+    if ((token_type == SEP_TT) && (token_value == ARROW_SEP))
+    {   if (nearby_flag)
+          error("The syntax '->' is only used as an alternative to 'Nearby'");
+
+        while ((token_type == SEP_TT) && (token_value == ARROW_SEP))
+        {   tree_depth++;
+            get_next_token();
+        }
+    }
+
+    sprintf(shortname_buffer, "?");
+
+    segment_markers.enabled = TRUE;
+
+    /*  This first word is either an internal name, or a textual short name,
+        or the end of the header part                                        */
+
+    if (end_of_header()) goto HeaderPassed;
+
+    if (token_type == DQ_TT) textual_name = token_text;
+    else
+    {   if ((token_type != SYMBOL_TT)
+            || (!(sflags[token_value] & UNKNOWN_SFLAG)))
+            ebf_error("name for new object or its textual short name",
+                token_text);
+        else
+        {   internal_name_symbol = token_value;
+            strcpy(internal_name, token_text);
+        }
+    }
+
+    /*  The next word is either a parent object, or
+        a textual short name, or the end of the header part                  */
+
+    get_next_token_with_directives();
+    if (end_of_header()) goto HeaderPassed;
+
+    if (token_type == DQ_TT)
+    {   if (textual_name != NULL)
+            error("Two textual short names given for only one object");
+        else
+            textual_name = token_text;
+    }
+    else
+    {   if ((token_type != SYMBOL_TT)
+            || (sflags[token_value] & UNKNOWN_SFLAG))
+        {   if (textual_name == NULL)
+                ebf_error("parent object or the object's textual short name",
+                    token_text);
+            else
+                ebf_error("parent object", token_text);
+        }
+        else goto SpecParent;
+    }
+
+    /*  Finally, it's possible that there is still a parent object           */
+
+    get_next_token();
+    if (end_of_header()) goto HeaderPassed;
+
+    if (specified_parent != -1)
+        ebf_error("body of object definition", token_text);
+    else
+    {   SpecParent:
+        if ((stypes[token_value] == OBJECT_T)
+            || (stypes[token_value] == CLASS_T))
+        {   specified_parent = svals[token_value];
+            sflags[token_value] |= USED_SFLAG;
+        }
+        else ebf_error("name of (the parent) object", token_text);
+    }
+
+    /*  Now it really has to be the body of the definition.                  */
+
+    get_next_token_with_directives();
+    if (end_of_header()) goto HeaderPassed;
+
+    ebf_error("body of object definition", token_text);
+
+    HeaderPassed:
+    if (specified_class == -1) put_token_back();
+
+    if (internal_name_symbol > 0)
+        assign_symbol(internal_name_symbol, no_objects + 1, OBJECT_T);
+
+    if (listobjects_switch)
+        printf("%3d \"%s\"\n", no_objects+1,
+            (textual_name==NULL)?"(with no short name)":textual_name);
+    if (textual_name == NULL)
+    {   if (internal_name_symbol > 0)
+            sprintf(shortname_buffer, "(%s)",
+                (char *) symbs[internal_name_symbol]);
+        else
+            sprintf(shortname_buffer, "(%d)", no_objects+1);
+    }
+    else
+    {   if (strlen(textual_name)>765)
+            error("Short name of object (in quotes) exceeded 765 characters");
+        strncpy(shortname_buffer, textual_name, 765);
+    }
+
+    if (specified_parent != -1)
+    {   if (tree_depth > 0)
+            error("Use of '->' (or 'Nearby') clashes with giving a parent");
+        parent_of_this_obj = specified_parent;
+    }
+    else
+    {   parent_of_this_obj = 0;
+        if (tree_depth>0)
+        {
+            /*  We have to set the parent object to the most recently defined
+                object at level (tree_depth - 1) in the tree.
+
+                A complication is that objects are numbered 1, 2, ... in the
+                Z-machine (and in the objects[].parent, etc., fields) but
+                0, 1, 2, ... internally (and as indices to object[]).        */
+
+            for (i=no_objects-1; i>=0; i--)
+            {   int j = i, k = 0;
+
+                /*  Metaclass or class objects cannot be '->' parents:  */
+                if ((!module_switch) && (i<4))
+                    continue;
+
+                if (!glulx_mode) {
+                    if (objectsz[i].parent == 1)
+                        continue;
+                    while (objectsz[j].parent != 0)
+                    {   j = objectsz[j].parent - 1; k++; }
+                }
+                else {
+                    if (objectsg[i].parent == 1)
+                        continue;
+                    while (objectsg[j].parent != 0)
+                    {   j = objectsg[j].parent - 1; k++; }
+                }
+
+                if (k == tree_depth - 1)
+                {   parent_of_this_obj = i+1;
+                    break;
+                }
+            }
+            if (parent_of_this_obj == 0)
+            {   if (tree_depth == 1)
+    error("'->' (or 'Nearby') fails because there is no previous object");
+                else
+    error("'-> -> ...' fails because no previous object is deep enough");
+            }
+        }
+    }
+
+    initialise_full_object();
+    if (instance_of != -1) add_class_to_inheritance_list(instance_of);
+
+    if (specified_class == -1) parse_body_of_definition();
+    else add_class_to_inheritance_list(specified_class);
+
+    if (debugfile_switch)
+    {   debug_file_printf("<object>");
+        if (internal_name_symbol > 0)
+        {   debug_file_printf("<identifier>%s</identifier>", internal_name);
+        } else
+        {   debug_file_printf
+                ("<identifier artificial=\"true\">%s</identifier>",
+                 internal_name);
+        }
+        debug_file_printf("<value>");
+        write_debug_object_backpatch(no_objects + 1);
+        debug_file_printf("</value>");
+        write_debug_locations
+            (get_token_location_end(beginning_debug_location));
+        debug_file_printf("</object>");
+    }
+
+    if (!glulx_mode)
+      manufacture_object_z();
+    else
+      manufacture_object_g();
+}
+
+/* ========================================================================= */
+/*   Data structure management routines                                      */
+/* ------------------------------------------------------------------------- */
+
+extern void init_objects_vars(void)
+{
+    properties_table = NULL;
+    prop_is_long = NULL;
+    prop_is_additive = NULL;
+    prop_default_value = NULL;
+
+    objectsz = NULL;
+    objectsg = NULL;
+    objectatts = NULL;
+    classes_to_inherit_from = NULL;
+    class_begins_at = NULL;
+}
+
+extern void objects_begin_pass(void)
+{
+    properties_table_size=0;
+    prop_is_long[1] = TRUE; prop_is_additive[1] = TRUE;            /* "name" */
+    prop_is_long[2] = TRUE; prop_is_additive[2] = TRUE;  /* inheritance prop */
+    if (!glulx_mode)
+        prop_is_long[3] = TRUE; prop_is_additive[3] = FALSE;
+                                         /* instance variables table address */
+    no_properties = 4;
+
+    if (debugfile_switch)
+    {   debug_file_printf("<property>");
+        debug_file_printf
+            ("<identifier artificial=\"true\">inheritance class</identifier>");
+        debug_file_printf("<value>2</value>");
+        debug_file_printf("</property>");
+        debug_file_printf("<property>");
+        debug_file_printf
+            ("<identifier artificial=\"true\">instance variables table address "
+             "(Z-code)</identifier>");
+        debug_file_printf("<value>3</value>");
+        debug_file_printf("</property>");
+    }
+
+    if (define_INFIX_switch) no_attributes = 1;
+    else no_attributes = 0;
+
+    no_objects = 0;
+    if (!glulx_mode) {
+        objectsz[0].parent = 0; objectsz[0].child = 0; objectsz[0].next = 0;
+        no_individual_properties=72;
+    }
+    else {
+        objectsg[0].parent = 0; objectsg[0].child = 0; objectsg[0].next = 0;
+        no_individual_properties = INDIV_PROP_START+8;
+    }
+    no_classes = 0;
+
+    no_embedded_routines = 0;
+
+    individuals_length=0;
+}
+
+extern void objects_allocate_arrays(void)
+{
+    objectsz = NULL;
+    objectsg = NULL;
+    objectatts = NULL;
+
+    prop_default_value    = my_calloc(sizeof(int32), INDIV_PROP_START,
+                                "property default values");
+    prop_is_long          = my_calloc(sizeof(int), INDIV_PROP_START,
+                                "property-is-long flags");
+    prop_is_additive      = my_calloc(sizeof(int), INDIV_PROP_START,
+                                "property-is-additive flags");
+
+    classes_to_inherit_from = my_calloc(sizeof(int), MAX_CLASSES,
+                                "inherited classes list");
+    class_begins_at       = my_calloc(sizeof(int32), MAX_CLASSES,
+                                "pointers to classes");
+    class_object_numbers  = my_calloc(sizeof(int),     MAX_CLASSES,
+                                "class object numbers");
+
+    properties_table      = my_malloc(MAX_PROP_TABLE_SIZE,"properties table");
+    individuals_table     = my_malloc(MAX_INDIV_PROP_TABLE_SIZE,
+                                "individual properties table");
+
+    defined_this_segment_size = 128;
+    defined_this_segment  = my_calloc(sizeof(int), defined_this_segment_size,
+                                "defined this segment table");
+
+    if (!glulx_mode) {
+      objectsz            = my_calloc(sizeof(objecttz), MAX_OBJECTS, 
+                                "z-objects");
+    }
+    else {
+      objectsg            = my_calloc(sizeof(objecttg), MAX_OBJECTS, 
+                                "g-objects");
+      objectatts          = my_calloc(NUM_ATTR_BYTES, MAX_OBJECTS, 
+                                "g-attributes");
+      full_object_g.props = my_calloc(sizeof(propg), MAX_OBJ_PROP_COUNT,
+                              "object property list");
+      full_object_g.propdata = my_calloc(sizeof(assembly_operand),
+                                 MAX_OBJ_PROP_TABLE_SIZE,
+                                 "object property data table");
+    }
+}
+
+extern void objects_free_arrays(void)
+{
+    my_free(&prop_default_value, "property default values");
+    my_free(&prop_is_long,     "property-is-long flags");
+    my_free(&prop_is_additive, "property-is-additive flags");
+
+    my_free(&objectsz,         "z-objects");
+    my_free(&objectsg,         "g-objects");
+    my_free(&objectatts,       "g-attributes");
+    my_free(&class_object_numbers,"class object numbers");
+    my_free(&classes_to_inherit_from, "inherited classes list");
+    my_free(&class_begins_at,  "pointers to classes");
+
+    my_free(&properties_table, "properties table");
+    my_free(&individuals_table,"individual properties table");
+
+    my_free(&defined_this_segment,"defined this segment table");
+
+    if (!glulx_mode) {
+        my_free(&full_object_g.props, "object property list");
+        my_free(&full_object_g.propdata, "object property data table");
+    }
+    
+}
+
+/* ========================================================================= */