Update to commit a469d404a7dc4e87e18f367eb4d8e05fc32d20a7
authorJason Self <j@jxself.org>
Sun, 29 May 2022 19:13:00 +0000 (12:13 -0700)
committerJason Self <j@jxself.org>
Sun, 29 May 2022 19:13:00 +0000 (12:13 -0700)
Dated May 28 2022. These changes are similiarly relicensed to GPL
per Section 4(c)(ii) of the Artistic License 2.0.

23 files changed:
DebugFileFormat.txt [deleted file]
src/arrays.c
src/asm.c
src/bpatch.c
src/chars.c
src/directs.c
src/errors.c
src/expressc.c
src/expressp.c
src/files.c
src/header.h
src/inform.c
src/lexer.c
src/linker.c
src/memory.c
src/objects.c
src/states.c
src/symbols.c
src/syntax.c
src/tables.c
src/text.c
src/veneer.c
src/verbs.c

diff --git a/DebugFileFormat.txt b/DebugFileFormat.txt
deleted file mode 100644 (file)
index fceef6c..0000000
+++ /dev/null
@@ -1,401 +0,0 @@
-Format of Inform 6 Debugging Information Files
-
-Version 1.0
-
-0: Introduction
-
-This is a specification of the Version 1 format for the debugging information
-files emitted by the Inform 6 compiler.  It replaces Version 0, which is
-documented in Section 12.5 of the Inform Technical Manual.
-
-1: Overview
-
-Debugging information files are written in XML and encoded in UTF-8.  They
-therefore begin with the following declaration:
-
-       <?xml version="1.0" encoding="UTF-8"?>
-
-Beyond the usual requirements for well-formed XML, the file adheres to the
-conventions that all numbers are written in decimal, all strings are
-case-sensitive, and all excerpts from binary files are Base64-encoded.
-
-2: The Top Level
-
-The root element is given by the tag <inform-story-file> with three attributes,
-the version of the debug file format being used, the name of the program that
-produced the file, and that program's version.  For instance,
-
-       <inform-story-file version="1.0" content-creator="Inform"
-                          content-creator-version="6.33">
-         ...
-       </inform-story-file>
-
-The elements from Sections 3--8 may appear in the ellipses.
-
-3: Story File Prefix
-
-The story file prefix contains a Base64 encoding of the story file's first bytes
-so that a debugging tool can easily check whether the story and the debug
-information file are mismatched.  For example, the prefix for a Glulx story
-might appear as
-
-       <story-file-prefix>
-         R2x1bAADAQEACqEAAAwsAAAMLAAAAQAAAAAAPAAIo2Jc
-         6B2XSW5mbwABAAA2LjMyMC4zOAABMTIxMDE1wQAAMA==
-       </story-file-prefix>
-
-The story file prefix is mandatory, but its length is unspecified.  Version 6.33
-of the Inform compiler records 64 bytes, which seems sufficient.
-
-4: Story File Sections
-
-Story file sections partition the story file according to how the data will be
-used.  For the Inform 6 compiler, this partitioning is the same as the one that
-the `z' flag prints.
-
-A record for a story file section gives a name for that section, its beginning
-address (inclusive), and its end address (exclusive):
-
-       <story-file-section>
-         <type>abbreviations table</type>
-         <address>64</address>
-         <end-address>128</end-address>
-       </story-file-section>
-
-The names currently in use include those from Section 12.5 of the Inform
-Technical Manual:
-
-       abbreviations table
-       header extension (Z-code only)
-       alphabets table (Z-code only)
-       Unicode table (Z-code only)
-       property defaults
-       object tree
-       common properties
-       class numbers
-       individual properties (Z-code only)
-       global variables
-       array space
-       grammar table
-       actions table
-       parsing routines (Z-code only)
-       adjectives table (Z-code only)
-       dictionary
-       code area
-       strings area
-
-plus one addition for Z-code:
-
-       abbreviations
-
-two additions for Glulx:
-
-       memory layout id
-       string decoding table
-
-and three additions for both targets:
-
-       header
-       identifier names
-       zero padding
-
-Names may repeat; Glulx story files, for example, sometimes have two zero
-padding sections.
-
-A compiler that does not wish to subdivide the story file should emit one
-section for the entirety and give it the name
-
-       story
-
-5: Source Files
-
-Source files are encoded as in the example below.  Each file has a unique index,
-which is used by other elements when referring to source code locations; these
-indices count from zero.  The file's path is recorded in two forms, first as it
-was given to the compiler via a command-line argument or include directive but
-without any path abbreviations like `>' (the form suitable for presentation to a
-human) and second after resolution to an absolute path (the form suitable for
-loading the file contents).  All paths are written according to the conventions
-of the host OS.  The language is, at present, either "Inform 6" or "Inform 7".
-More languages may added in the future.
-
-       <source index="0">
-         <given-path>example.inf</given-path>
-         <resolved-path>/home/user/directory/example.inf</resolved-path>
-         <language>Inform 6</language>
-       </source>
-
-If the source file is known to appear in the story's Blorb, its chunk number
-will also be recorded:
-
-       <source index="0">
-         <given-path>example.inf</given-path>
-         <resolved-path>/home/user/directory/example.inf</resolved-path>
-         <language>Inform 6</language>
-         <blorb-chunk-number>18</blorb-chunk-number>
-       </source>
-
-6: Table Entries; Grammar Lines
-
-Table entries are data defined by particular parts of the source code, but
-without any corresponding identifiers.  The <table-entry> element notes the
-entry's type, the address where it begins (inclusive), the address where it ends
-(exclusive), and the defining source code location(s), if any:
-
-       <table-entry>
-         <type>grammar line</type>
-         <address>1004</address>
-         <end-address>1030</end-address>
-         <source-code-location>...</source-code-location>
-       </table-entry>
-
-Version 6.33 of the Inform compiler only emits <table-entry> tags for grammar
-lines; these data are all located in the grammar table section.
-
-7: Named Values; Constants, Attributes, Properties, Actions, Fake Actions,
-   Objects, Classes, Arrays, and Routines
-
-Records for named values store their identifier, their value, and the source
-code location(s) of their definition, if any.  For instance,
-
-       <constant>
-         <identifier>MAX_SCORE</identifier>
-         <value>40</value>
-         <source-code-location>...</source-code-location>
-       </constant>
-
-would represent a named constant.  Attributes, properties, actions, fake
-actions, objects, arrays, and routines are also names for numbers, and differ
-only in their use; they are represented in the same format under the tags
-<attribute>, <property>, <action>, <fake-action>, <object>, <array>, and
-<routine>.  (Moreover, unlike Version 0 of the debug information format, fake
-actions are not recorded as both fake actions and actions.)
-
-The records for constants include some extra entries for the system constants
-tabulated in Section 12.2 of the Inform Technical Manual, even though these are
-not created by Constant directives.  Entries for #undefed constants are also
-included, but necessarily without values.
-
-Some records for objects will represent class objects.  In that case, they will
-be given with the tag <class> rather than <object> and include an additional
-child to indicate their class number:
-
-       <class>
-         <identifier>lamp</identifier>
-         <class-number>5</class-number>
-         <value>1560</value>
-         <source-code-location>...</source-code-location>
-       </class>
-
-Records for arrays also have extra children, which record their size, their
-element size, and the intended semantics for their zeroth element:
-
-       <array>
-         <identifier>route</identifier>
-         <value>1500</value>
-         <byte-count>20</byte-count>
-         <bytes-per-element>4</bytes-per-element>
-         <zeroth-element-holds-length>true</zeroth-element-holds-length>
-         <source-code-location>...</source-code-location>
-       </array>
-
-And finally, <routine> records contain an <address> and a <byte-count> element,
-along with any number of the <local-variable> and <sequence-point> elements,
-which are described in Sections 9 and 10.  The address is provided because the
-identifier's value may be packed.
-
-Sometimes what would otherwise be a named value is in fact anonymous; unnamed
-objects, embedded routines, some replaced routines, veneer properties, and the
-Infix attribute are all examples.  In such a case, the <identifier> subelement
-will carry the XML attribute
-
-       artificial
-
-to indicate that the compiler is providing a sensible name of its own, which
-could be presented to a human, but is not actually an identifier.  For instance:
-
-       <routine>
-         <identifier artificial="true">lantern.time_left</identifier>
-         <value>1820</value>
-         <byte-count>80</byte-count>
-         <source-code-location>...</source-code-location>
-         ...
-       </routine>
-
-Artificial identifiers may contain characters, like the full stop in
-``lantern.time_left'', that would not be legal in the source language.
-
-8: Global Variables
-
-Globals are similar to named values, except that they are not interpreted as a
-fixed value, but rather have an address where their value can be found.  Their
-records therefore contain an <address> tag in place of the <value> tag, as in:
-
-       <global-variable>
-         <identifier>darkness_witnessed</identifier>
-         <address>1520</address>
-         <source-code-location>...</source-code-location>
-       </global-variable>
-
-9: Local Variables
-
-The format for local variables mimics the format for global variables, except
-that a source code location is never included, and their memory locations are
-not given by address.  For Z-code, locals are specified by index:
-
-       <local-variable>
-         <identifier>parameter</identifier>
-         <index>1</index>
-       </local-variable>
-
-whereas for Glulx they are specified by frame offset:
-
-       <local-variable>
-         <identifier>parameter</identifier>
-         <frame-offset>4</frame-offset>
-       </local-variable>
-
-If a local variable identifier is only in scope for part of a routine, it's
-scope will be encoded as a beginning instruction address (inclusive) and an
-ending instruction address (exclusive):
-
-       <local-variable>
-         <identifier>rulebook</identifier>
-         <index>0</index>
-         <scope-address>1628</scope-address>
-         <end-scope-address>1678</end-scope-address>
-       </local-variable>
-
-Identifiers with noncontiguous scopes are recorded as one <local-variable>
-element per contiguous region.  It is possible for the same identifier to map to
-different variables, so long as the corresponding scopes are disjoint.
-
-10: Sequence Points
-
-Sequence points are stored as an instruction address and the corresponding
-single location in the source code:
-
-       <sequence-point>
-         <address>1628</address>
-         <source-code-location>...</source-code-location>
-       </sequence-point>
-
-The source code location will always be exactly one position with overlapping
-endpoints.
-
-Sequence points are defined as in Section 12.4 of the Inform Technical Manual,
-but with the further stipulation that labels do not influence their source code
-locations, as they did in Version 0 of the debug information format.  For
-instance, in code like
-
-       say__p = 1; ParaContent(); .L_Say59; .LSayX59;
-       t_0 = 0;
-
-the sequence points are to be placed like this:
-
-       <*> say__p = 1; <*> ParaContent(); .L_Say59; .LSayX59;
-       <*> t_0 = 0;
-
-rather than like this:
-
-       <*> say__p = 1; <*> ParaContent(); <*> .L_Say59; .LSayX59;
-       t_0 = 0;
-
-11: Source Code Locations
-
-Most source code locations take the following format, which describes their
-file, the line and character number where they begin (inclusive), the line and
-character number where they end (exclusive), and the file positions (in bytes)
-corresponding to those endpoints:
-
-       <source-code-location>
-         <file-index>0</file-index>
-         <line>1024</line>
-         <character>4</character>
-         <file-position>44153</file-position>
-         <end-line>1025</end-line>
-         <end-character>1</end-character>
-         <end-file-position>44186</end-file-position>
-       </source-code-location>
-
-Line numbers and character numbers begin at one, but file positions count from
-zero.
-
-In the special case where the endpoints coincide, as happens with sequence
-points, the end elements may be elided:
-
-       <source-code-location>
-         <file-index>0</file-index>
-         <line>1024</line>
-         <character>4</character>
-         <file-position>44153</file-position>
-       </source-code-location>
-
-At the other extreme, sometimes definitions span several source files or appear
-in two different languages.  The former case is dealt with by including multiple
-code location elements and indexing them to indicate order:
-
-       <!-- First Part of Inform 6 Definition -->
-       <source-code-location index="0">
-         <!-- Assuming file 0 was given with the language "Inform 6" -->
-         <file-index>0</file-index>
-         <line>1024</line>
-         <character>4</character>
-         <file-position>44153</file-position>
-         <end-line>1025</end-line>
-         <end-character>1</end-character>
-         <end-file-position>44186</end-file-position>
-       </source-code-location>
-       <!-- Second Part of Inform 6 Definition -->
-       <source-code-location index="1">
-         <!-- Assuming file 1 was given with the language "Inform 6" -->
-         <file-index>1</file-index>
-         <line>1</line>
-         <character>0</character>
-         <file-position>0</file-position>
-         <end-line>3</end-line>
-         <end-character>1</end-character>
-         <end-file-position>59</end-file-position>
-       </source-code-location>
-
-The latter case is also handled with multiple elements.  Note that indexing is
-only used to indicated order among locations in the same language.
-
-       <!-- Inform 7 Definition -->
-       <source-code-location>
-         <!-- Assuming file 2 was given with the language "Inform 7" -->
-         <file-index>2</file-index>
-         <line>12</line>
-         <character>0</character>
-         <file-position>308</file-position>
-         <end-line>12</end-line>
-         <end-character>112</end-character>
-         <end-file-position>420</end-file-position>
-       </source-code-location>
-       <!-- Inform 6 Definition -->
-       <source-code-location>
-         <!-- Assuming file 0 was given with the language "Inform 6" -->
-         <file-index>0</file-index>
-         <line>1024</line>
-         <character>4</character>
-         <file-position>44153</file-position>
-         <end-line>1025</end-line>
-         <end-character>1</end-character>
-         <end-file-position>44186</end-file-position>
-       </source-code-location>
-
---
-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/
\ No newline at end of file
index 9c001ccb0ed57ec678f1c6542910ac257c8be8c2..320287b3c0c5fef909acd4f0355a26c5fd3cff3e 100644 (file)
@@ -3,8 +3,8 @@
 /*               likewise global variables, which are in some ways a         */
 /*               simpler form of the same thing.                             */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
 /* ------------------------------------------------------------------------- */
 /*   Arrays defined below:                                                   */
 /*                                                                           */
-/*    int    dynamic_array_area[]         Initial values for the bytes of    */
+/*    uchar  dynamic_array_area[]         Initial values for the bytes of    */
 /*                                        the dynamic array area             */
-/*    int    static_array_area[]          Initial values for the bytes of    */
+/*    uchar  static_array_area[]          Initial values for the bytes of    */
 /*                                        the static array area              */
 /*    int32  global_initial_value[n]      The initialised value of the nth   */
-/*                                        global variable (counting 0 - 239) */
+/*                                        global variable (counting 0 - 239, */
+/*                                        or higher for Glulx)               */
 /*                                                                           */
 /*   The "dynamic array area" is the Z-machine area holding the current      */
 /*   values of the global variables (in 240x2 = 480 bytes) followed by any   */
-/*   (dynamic) arrays which may be defined.  Owing to a poor choice of name  */
-/*   some years ago, this is also called the "static data area", which is    */
-/*   why the memory setting for its maximum extent is "MAX_STATIC_DATA".     */
+/*   (dynamic) arrays which may be defined.                                  */
 /*                                                                           */
-/*   In Glulx, that 240 is changed to MAX_GLOBAL_VAR_NUMBER, and we take     */
-/*   correspondingly more space for the globals. This *really* ought to be   */
-/*   split into two segments.                                                */
+/*   In Glulx, we don't keep the global variables in dynamic_array_area.     */
+/*   Array data starts at the start.                                         */
 /*                                                                           */
 /*   We can also store arrays (but not globals) into static memory (ROM).    */
-/*   The storage for these goes, unsurprisingly, into static_array_area --   */
-/*   a separate allocation of MAX_STATIC_DATA bytes.                         */
+/*   The storage for these goes, unsurprisingly, into static_array_area.     */
 /* ------------------------------------------------------------------------- */
-int     *dynamic_array_area;           /* See above                          */
-int32   *global_initial_value;
+uchar   *dynamic_array_area;           /* See above                          */
+memory_list dynamic_array_area_memlist;
+int dynamic_array_area_size;           /* Size in bytes                      */
+
+int32   *global_initial_value;         /* Allocated to no_globals            */
+static memory_list global_initial_value_memlist;
 
 int no_globals;                        /* Number of global variables used
                                           by the programmer (Inform itself
@@ -57,17 +58,13 @@ int no_globals;                        /* Number of global variables used
                                        /* In Glulx, Inform uses the bottom 
                                           ten.                               */
 
-int dynamic_array_area_size;           /* Size in bytes                      */
-
-int     *static_array_area;
+uchar   *static_array_area;
+memory_list static_array_area_memlist;
 int static_array_area_size;
 
 int no_arrays;
-int32   *array_symbols;
-int     *array_sizes, *array_types, *array_locs;
-/* array_sizes[N] gives the length of array N; array_types[N] is one of
-   the constants BYTE_ARRAY, WORD_ARRAY, etc; array_locs[N] is true for
-   static arrays, false for dynamic arrays.                                  */
+arrayinfo *arrays;
+static memory_list arrays_memlist;
 
 static int array_entry_size,           /* 1 for byte array, 2 for word array */
            array_base;                 /* Offset in dynamic array area of the
@@ -81,16 +78,24 @@ static int array_entry_size,           /* 1 for byte array, 2 for word array */
                                        /* In Glulx, of course, that will be
                                           4 instead of 2.                    */
 
+static memory_list current_array_name; /* The name of the global or array
+                                          currently being compiled.          */
+
+/* Complete the array. Fill in the size field (if it has one) and 
+   advance foo_array_area_size.
+*/
 extern void finish_array(int32 i, int is_static)
 {
-  int *area;
+  uchar *area;
   int area_size;
   
   if (!is_static) {
+      ensure_memory_list_available(&dynamic_array_area_memlist, dynamic_array_area_size+array_base+1*array_entry_size);
       area = dynamic_array_area;
       area_size = dynamic_array_area_size;
   }
   else {
+      ensure_memory_list_available(&static_array_area_memlist, static_array_area_size+array_base+1*array_entry_size);
       area = static_array_area;
       area_size = static_array_area_size;
   }
@@ -146,16 +151,22 @@ extern void finish_array(int32 i, int is_static)
 
 }
 
+/* Fill in array entry i (in either the static or dynamic area).
+   When this is called, foo_array_area_size is the end of the previous
+   array; we're writing after that.
+*/
 extern void array_entry(int32 i, int is_static, assembly_operand VAL)
 {
-  int *area;
+  uchar *area;
   int area_size;
   
   if (!is_static) {
+      ensure_memory_list_available(&dynamic_array_area_memlist, dynamic_array_area_size+(i+1)*array_entry_size);
       area = dynamic_array_area;
       area_size = dynamic_array_area_size;
   }
   else {
+      ensure_memory_list_available(&static_array_area_memlist, static_array_area_size+(i+1)*array_entry_size);
       area = static_array_area;
       area_size = static_array_area_size;
   }
@@ -163,9 +174,6 @@ extern void array_entry(int32 i, int is_static, assembly_operand VAL)
   if (!glulx_mode) {
     /*  Array entry i (initial entry has i=0) is set to Z-machine value j    */
 
-    if (area_size+(i+1)*array_entry_size > MAX_STATIC_DATA)
-        memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA);
-
     if (array_entry_size==1)
     {   area[area_size+i] = (VAL.value)%256;
 
@@ -198,9 +206,6 @@ extern void array_entry(int32 i, int is_static, assembly_operand VAL)
   else {
     /*  Array entry i (initial entry has i=0) is set to value j              */
 
-    if (area_size+(i+1)*array_entry_size > MAX_STATIC_DATA)
-        memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA);
-
     if (array_entry_size==1)
     {   area[area_size+i] = (VAL.value) & 0xFF;
 
@@ -223,22 +228,19 @@ extern void array_entry(int32 i, int is_static, assembly_operand VAL)
         if (VAL.marker != 0) {
             if (!is_static) {
                 backpatch_zmachine(VAL.marker, DYNAMIC_ARRAY_ZA,
-                    addr - 4*MAX_GLOBAL_VARIABLES);
+                    addr);
             }
             else {
                 /* We can't use backpatch_zmachine() because that only applies to RAM. Instead we add an entry to staticarray_backpatch_table.
                    A backpatch entry is five bytes: *_MV followed by the array offset (in static array area). */
-                write_byte_to_memory_block(&staticarray_backpatch_table,
-                    staticarray_backpatch_size++,
-                    VAL.marker);
-                write_byte_to_memory_block(&staticarray_backpatch_table,
-                    staticarray_backpatch_size++, ((addr >> 24) & 0xFF));
-                write_byte_to_memory_block(&staticarray_backpatch_table,
-                    staticarray_backpatch_size++, ((addr >> 16) & 0xFF));
-                write_byte_to_memory_block(&staticarray_backpatch_table,
-                    staticarray_backpatch_size++, ((addr >> 8) & 0xFF));
-                write_byte_to_memory_block(&staticarray_backpatch_table,
-                    staticarray_backpatch_size++, (addr & 0xFF));
+                if (bpatch_trace_setting >= 2)
+                    printf("BP added: MV %d staticarray %04x\n", VAL.marker, addr);
+                ensure_memory_list_available(&staticarray_backpatch_table_memlist, staticarray_backpatch_size+5);
+                staticarray_backpatch_table[staticarray_backpatch_size++] = VAL.marker;
+                staticarray_backpatch_table[staticarray_backpatch_size++] = ((addr >> 24) & 0xFF);
+                staticarray_backpatch_table[staticarray_backpatch_size++] = ((addr >> 16) & 0xFF);
+                staticarray_backpatch_table[staticarray_backpatch_size++] = ((addr >> 8) & 0xFF);
+                staticarray_backpatch_table[staticarray_backpatch_size++] = (addr & 0xFF);
             }
         }
     }
@@ -271,7 +273,11 @@ extern void array_entry(int32 i, int is_static, assembly_operand VAL)
 /* ------------------------------------------------------------------------- */
 
 extern void set_variable_value(int i, int32 v)
-{   global_initial_value[i]=v;
+{
+    /* This can be called during module-load to create a new global,
+       so we call ensure. */
+    ensure_memory_list_available(&global_initial_value_memlist, i+1);
+    global_initial_value[i]=v;
 }
 
 /*  There are four ways to initialise arrays:                                */
@@ -289,6 +295,7 @@ extern void make_global(int array_flag, int name_only)
         array_flag is always FALSE in that case.                             */
 
     int32 i;
+    int name_length;
     int array_type, data_type;
     int is_static = FALSE;
     assembly_operand AO;
@@ -296,7 +303,6 @@ extern void make_global(int array_flag, int name_only)
     int extraspace;
 
     int32 global_symbol;
-    const char *global_name;
     debug_location_beginning beginning_debug_location =
         get_token_location_beginning();
 
@@ -304,15 +310,18 @@ extern void make_global(int array_flag, int name_only)
     get_next_token();
     i = token_value;
     global_symbol = i;
-    global_name = token_text;
+    
+    name_length = strlen(token_text) + 1;
+    ensure_memory_list_available(&current_array_name, name_length);
+    strncpy(current_array_name.data, token_text, name_length);
 
     if (!glulx_mode) {
-        if ((token_type==SYMBOL_TT) && (stypes[i]==GLOBAL_VARIABLE_T)
-            && (svals[i] >= LOWEST_SYSTEM_VAR_NUMBER))
+        if ((token_type==SYMBOL_TT) && (symbols[i].type==GLOBAL_VARIABLE_T)
+            && (symbols[i].value >= LOWEST_SYSTEM_VAR_NUMBER))
             goto RedefinitionOfSystemVar;
     }
     else {
-        if ((token_type==SYMBOL_TT) && (stypes[i]==GLOBAL_VARIABLE_T))
+        if ((token_type==SYMBOL_TT) && (symbols[i].type==GLOBAL_VARIABLE_T))
             goto RedefinitionOfSystemVar;
     }
 
@@ -324,15 +333,15 @@ extern void make_global(int array_flag, int name_only)
         panic_mode_error_recovery(); return;
     }
 
-    if (!(sflags[i] & UNKNOWN_SFLAG))
+    if (!(symbols[i].flags & UNKNOWN_SFLAG))
     {   discard_token_location(beginning_debug_location);
         if (array_flag)
-            ebf_symbol_error("new array name", token_text, typename(stypes[i]), slines[i]);
-        else ebf_symbol_error("new global variable name", token_text, typename(stypes[i]), slines[i]);
+            ebf_symbol_error("new array name", token_text, typename(symbols[i].type), symbols[i].line);
+        else ebf_symbol_error("new global variable name", token_text, typename(symbols[i].type), symbols[i].line);
         panic_mode_error_recovery(); return;
     }
 
-    if ((!array_flag) && (sflags[i] & USED_SFLAG))
+    if ((!array_flag) && (symbols[i].flags & USED_SFLAG))
         error_named("Variable must be defined before use:", token_text);
 
     directive_keywords.enabled = TRUE;
@@ -352,18 +361,13 @@ extern void make_global(int array_flag, int name_only)
     
     if (array_flag)
     {   if (!is_static) {
-            if (!glulx_mode)
-                assign_symbol(i, dynamic_array_area_size, ARRAY_T);
-            else
-                assign_symbol(i, 
-                    dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES, ARRAY_T);
+            assign_symbol(i, dynamic_array_area_size, ARRAY_T);
         }
         else {
             assign_symbol(i, static_array_area_size, STATIC_ARRAY_T);
         }
-        if (no_arrays == MAX_ARRAYS)
-            memoryerror("MAX_ARRAYS", MAX_ARRAYS);
-        array_symbols[no_arrays] = i;
+        ensure_memory_list_available(&arrays_memlist, no_arrays+1);
+        arrays[no_arrays].symbol = i;
     }
     else
     {   if (!glulx_mode && no_globals==233)
@@ -372,19 +376,19 @@ extern void make_global(int array_flag, int name_only)
             panic_mode_error_recovery();
             return;
         }
-        if (glulx_mode && no_globals==MAX_GLOBAL_VARIABLES)
-        {   discard_token_location(beginning_debug_location);
-            memoryerror("MAX_GLOBAL_VARIABLES", MAX_GLOBAL_VARIABLES);
-            panic_mode_error_recovery();
-            return;
-        }
-
-        variable_tokens[MAX_LOCAL_VARIABLES+no_globals] = i;
+        
+        ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES+no_globals+1);
+        variables[MAX_LOCAL_VARIABLES+no_globals].token = i;
+        variables[MAX_LOCAL_VARIABLES+no_globals].usage = FALSE;
         assign_symbol(i, MAX_LOCAL_VARIABLES+no_globals, GLOBAL_VARIABLE_T);
-        variable_tokens[svals[i]] = i;
 
-        if (name_only) import_symbol(i);
-        else global_initial_value[no_globals++]=0;
+        if (name_only) {
+            import_symbol(i);
+        }
+        else {
+            ensure_memory_list_available(&global_initial_value_memlist, no_globals+1);
+            global_initial_value[no_globals++]=0;
+        }
     }
 
     directive_keywords.enabled = TRUE;
@@ -405,10 +409,12 @@ extern void make_global(int array_flag, int name_only)
         }
         put_token_back();
         if (debugfile_switch && !array_flag)
-        {   debug_file_printf("<global-variable>");
+        {
+            char *global_name = current_array_name.data;
+            debug_file_printf("<global-variable>");
             debug_file_printf("<identifier>%s</identifier>", global_name);
             debug_file_printf("<address>");
-            write_debug_global_backpatch(svals[global_symbol]);
+            write_debug_global_backpatch(symbols[global_symbol].value);
             debug_file_printf("</address>");
             write_debug_locations
                 (get_token_location_end(beginning_debug_location));
@@ -434,10 +440,12 @@ extern void make_global(int array_flag, int name_only)
             }
             global_initial_value[no_globals-1] = AO.value;
             if (debugfile_switch)
-            {   debug_file_printf("<global-variable>");
+            {
+                char *global_name = current_array_name.data;
+                debug_file_printf("<global-variable>");
                 debug_file_printf("<identifier>%s</identifier>", global_name);
                 debug_file_printf("<address>");
-                write_debug_global_backpatch(svals[global_symbol]);
+                write_debug_global_backpatch(symbols[global_symbol].value);
                 debug_file_printf("</address>");
                 write_debug_locations
                     (get_token_location_end(beginning_debug_location));
@@ -456,7 +464,7 @@ extern void make_global(int array_flag, int name_only)
         else {
             backpatch_zmachine(ARRAY_MV, GLOBALVAR_ZA, 4*(no_globals-1));
             global_initial_value[no_globals-1]
-                = dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES;
+                = dynamic_array_area_size;
         }
     }
 
@@ -542,9 +550,12 @@ extern void make_global(int array_flag, int name_only)
         static_array_area_size += extraspace;
     }
 
-    array_types[no_arrays] = array_type;
-    array_locs[no_arrays] = is_static;
+    arrays[no_arrays].type = array_type;
+    arrays[no_arrays].loc = is_static;
 
+    /* Note that, from this point, we must continue through finish_array().
+       Exiting this routine on error causes problems. */
+    
     switch(data_type)
     {
         case NULLS_AI:
@@ -582,7 +593,12 @@ extern void make_global(int array_flag, int name_only)
 
             i=0;
             do
-            {   get_next_token();
+            {
+                /* This isn't the start of a statement, but it's safe to
+                   release token texts anyway. Expressions in an array
+                   list are independent of each other. */
+                release_token_texts();
+                get_next_token();
                 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
                     break;
 
@@ -592,7 +608,6 @@ extern void make_global(int array_flag, int name_only)
                 {   discard_token_location(beginning_debug_location);
                     error("Missing ';' to end the initial array values "
                           "before \"[\" or \"]\"");
-                    return;
                 }
                 put_token_back();
 
@@ -669,7 +684,12 @@ advance as part of 'Zcharacter table':", unicode);
 
             i = 0;
             while (TRUE)
-            {   get_next_token();
+            {
+                /* This isn't the start of a statement, but it's safe to
+                   release token texts anyway. Expressions in an array
+                   list are independent of each other. */
+                release_token_texts();
+                get_next_token();
                 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
                     continue;
                 if ((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP))
@@ -693,10 +713,11 @@ advance as part of 'Zcharacter table':", unicode);
     if (debugfile_switch)
     {
         int32 new_area_size;
+        char *global_name = current_array_name.data;
         debug_file_printf("<array>");
         debug_file_printf("<identifier>%s</identifier>", global_name);
         debug_file_printf("<value>");
-        write_debug_array_backpatch(svals[global_symbol]);
+        write_debug_array_backpatch(symbols[global_symbol].value);
         debug_file_printf("</value>");
         new_area_size = (!is_static ? dynamic_array_area_size : static_array_area_size);
         debug_file_printf
@@ -717,7 +738,7 @@ advance as part of 'Zcharacter table':", unicode);
 
     if ((array_type==BYTE_ARRAY) || (array_type==WORD_ARRAY)) i--;
     if (array_type==BUFFER_ARRAY) i+=WORDSIZE-1;
-    array_sizes[no_arrays++] = i;
+    arrays[no_arrays++].size = i;
 }
 
 extern int32 begin_table_array(void)
@@ -733,10 +754,7 @@ extern int32 begin_table_array(void)
 
     dynamic_array_area_size += array_entry_size;
 
-    if (!glulx_mode)
-        return array_base;
-    else
-        return array_base - WORDSIZE * MAX_GLOBAL_VARIABLES;
+    return array_base;
 }
 
 extern int32 begin_word_array(void)
@@ -748,10 +766,7 @@ extern int32 begin_word_array(void)
     array_base = dynamic_array_area_size;
     array_entry_size = WORDSIZE;
 
-    if (!glulx_mode)
-        return array_base;
-    else
-        return array_base - WORDSIZE * MAX_GLOBAL_VARIABLES;
+    return array_base;
 }
 
 /* ========================================================================= */
@@ -761,41 +776,85 @@ extern int32 begin_word_array(void)
 extern void init_arrays_vars(void)
 {   dynamic_array_area = NULL;
     static_array_area = NULL;
+    arrays = NULL;
     global_initial_value = NULL;
-    array_sizes = NULL; array_symbols = NULL; array_types = NULL;
+    variables = NULL;
 }
 
 extern void arrays_begin_pass(void)
-{   no_arrays = 0; 
-    if (!glulx_mode)
-        no_globals=0; 
-    else
-        no_globals=11;
-    dynamic_array_area_size = WORDSIZE * MAX_GLOBAL_VARIABLES;
+{
+    int ix, totalvar;
+    
+    no_arrays = 0; 
+    if (!glulx_mode) {
+        no_globals = 0;
+        /* The compiler-defined globals start at 239 and go down, so
+           we need to initialize the entire list from the start. */
+        totalvar = MAX_ZCODE_GLOBAL_VARS;
+    }
+    else {
+        /* The compiler-defined globals run from 0 to 10. */
+        no_globals = 11;
+        totalvar = no_globals;
+    }
+    
+    ensure_memory_list_available(&global_initial_value_memlist, totalvar);
+    for (ix=0; ix<totalvar; ix++) {
+        global_initial_value[ix] = 0;
+    }
+    
+    ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES+totalvar);
+    for (ix=0; ix<MAX_LOCAL_VARIABLES+totalvar; ix++) {
+        variables[ix].token = 0;
+        variables[ix].usage = FALSE;
+    }
+    
+    dynamic_array_area_size = 0;
+
+    if (!glulx_mode) {
+        int ix;
+        /* This initial segment of dynamic_array_area is never used. It's
+           notionally space for the global variables, but that data is
+           kept in the global_initial_value array. Nonetheless, all the
+           Z-compiler math is set up with the idea that arrays start at
+           WORDSIZE * MAX_ZCODE_GLOBAL_VARS, so we need the blank segment.
+        */
+        dynamic_array_area_size = WORDSIZE * MAX_ZCODE_GLOBAL_VARS;
+        ensure_memory_list_available(&dynamic_array_area_memlist, dynamic_array_area_size);
+        for (ix=0; ix<WORDSIZE * MAX_ZCODE_GLOBAL_VARS; ix++)
+            dynamic_array_area[ix] = 0;
+    }
+    
     static_array_area_size = 0;
 }
 
 extern void arrays_allocate_arrays(void)
-{   dynamic_array_area = my_calloc(sizeof(int), MAX_STATIC_DATA, 
+{
+    initialise_memory_list(&dynamic_array_area_memlist,
+        sizeof(uchar), 10000, (void**)&dynamic_array_area,
         "dynamic array data");
-    static_array_area = my_calloc(sizeof(int), MAX_STATIC_DATA, 
+    initialise_memory_list(&static_array_area_memlist,
+        sizeof(uchar), 0, (void**)&static_array_area,
         "static array data");
-    array_sizes = my_calloc(sizeof(int), MAX_ARRAYS, "array sizes");
-    array_types = my_calloc(sizeof(int), MAX_ARRAYS, "array types");
-    array_locs = my_calloc(sizeof(int), MAX_ARRAYS, "array locations");
-    array_symbols = my_calloc(sizeof(int32), MAX_ARRAYS, "array symbols");
-    global_initial_value = my_calloc(sizeof(int32), MAX_GLOBAL_VARIABLES, 
-        "global values");
+    initialise_memory_list(&arrays_memlist,
+        sizeof(arrayinfo), 64, (void**)&arrays,
+        "array info");
+    initialise_memory_list(&global_initial_value_memlist,
+        sizeof(int32), 200, (void**)&global_initial_value,
+        "global variable values");
+
+    initialise_memory_list(&current_array_name,
+        sizeof(char), MAX_IDENTIFIER_LENGTH+1, NULL,
+        "array name currently being defined");
 }
 
 extern void arrays_free_arrays(void)
-{   my_free(&dynamic_array_area, "dynamic array data");
-    my_free(&static_array_area, "static array data");
-    my_free(&global_initial_value, "global values");
-    my_free(&array_sizes, "array sizes");
-    my_free(&array_types, "array types");
-    my_free(&array_locs, "array locations");
-    my_free(&array_symbols, "array sizes");
+{
+    deallocate_memory_list(&dynamic_array_area_memlist);
+    deallocate_memory_list(&static_array_area_memlist);
+    deallocate_memory_list(&arrays_memlist);
+    deallocate_memory_list(&global_initial_value_memlist);
+    deallocate_memory_list(&current_array_name);
 }
 
 /* ========================================================================= */
index 6a263a311b6196ec327fcbd6d2cbc72a5ff307e3..3c53096c05956f084497ed8d8d020459a1ff91eb 100644 (file)
--- a/src/asm.c
+++ b/src/asm.c
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "asm" : The Inform assembler                                            */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
 
 #include "header.h"
 
-uchar *zcode_holding_area;         /* Area holding code yet to be transferred
+static uchar *zcode_holding_area;  /* Area holding code yet to be transferred
                                       to either zcode_area or temp file no 1 */
-uchar *zcode_markers;              /* Bytes holding marker values for this
+static memory_list zcode_holding_area_memlist;
+static uchar *zcode_markers;       /* Bytes holding marker values for this
                                       code                                   */
+static memory_list zcode_markers_memlist;
 static int zcode_ha_size;          /* Number of bytes in holding area        */
 
-memory_block zcode_area;           /* Block to hold assembled code (if
-                                      temporary files are not being used)    */
+uchar *zcode_area;                 /* Array to hold assembled code           */
+
+memory_list zcode_area_memlist;    /* Manages zcode_area                     */
 
 int32 zmachine_pc;                 /* PC position of assembly (byte offset
                                       from start of Z-code area)             */
 
 int32 no_instructions;             /* Number of instructions assembled       */
-int execution_never_reaches_here,  /* TRUE if the current PC value in the
+int execution_never_reaches_here;  /* nonzero if the current PC value in the
                                       code area cannot be reached: e.g. if
                                       the previous instruction was a "quit"
-                                      opcode and no label is set to here     */
-    next_label,                    /* Used to count the labels created all
+                                      opcode and no label is set to here
+                                      (see EXECSTATE flags for more) */
+int next_label,                    /* Used to count the labels created all
                                       over Inform in current routine, from 0 */
     next_sequence_point;           /* Likewise, for sequence points          */
-int no_sequence_points;            /* Kept for statistics purposes only      */
+int no_sequence_points;            /* Total over all routines; kept for
+                                      statistics purposes only               */
 
 static int label_moved_error_already_given;
                                    /* When one label has moved, all subsequent
@@ -60,18 +65,17 @@ int uses_acceleration_features;    /* Makes use of Glulx acceleration (3.1.1)
                                       features?                              */
 int uses_float_features;           /* Makes use of Glulx floating-point (3.1.2)
                                       features?                              */
+int uses_extundo_features;         /* Makes use of Glulx extended undo (3.1.3)
+                                      features?                              */
 
 debug_location statement_debug_location;
                                    /* Location of current statement          */
 
 
-int32 *variable_tokens;            /* The allocated size is 
-                                      (MAX_LOCAL_VARIABLES +
-                                      MAX_GLOBAL_VARIABLES). The entries 
-                                      MAX_LOCAL_VARIABLES and up give the 
-                                      symbol table index for the names of 
-                                      the global variables                   */
-int *variable_usage;               /* TRUE if referred to, FALSE otherwise   */
+variableinfo *variables;           /* The allocated size is 
+                                      (MAX_LOCAL_VARIABLES + no_globals).
+                                      Local variables first, then globals.   */
+memory_list variables_memlist;
 
 assembly_instruction AI;           /* A structure used to hold the full
                                       specification of a single Z-code
@@ -85,14 +89,17 @@ static char opcode_syntax_string[128];  /*  Text buffer holding the correct
 
 static int routine_symbol;         /* The symbol index of the routine currently
                                       being compiled */
-static char *routine_name;         /* The name of the routine currently being
-                                      compiled                               */
+static memory_list current_routine_name; /* The name of the routine currently
+                                      being compiled. (This may be longer
+                                      than MAX_IDENTIFIER_LENGTH, e.g. for
+                                      an "obj.prop" property routine.)       */
 static int routine_locals;         /* The number of local variables used by
                                       the routine currently being compiled   */
 
 static int32 routine_start_pc;
 
-int32 *named_routine_symbols;
+int32 *named_routine_symbols;      /* Allocated up to no_named_routines      */
+static memory_list named_routine_symbols_memlist;
 
 static void transfer_routine_z(void);
 static void transfer_routine_g(void);
@@ -101,34 +108,158 @@ static void transfer_routine_g(void);
 /*   Label data                                                              */
 /* ------------------------------------------------------------------------- */
 
+static labelinfo *labels; /* Label offsets  (i.e. zmachine_pc values).
+                             These are allocated sequentially, but accessed
+                             as a double-linked list from first_label
+                             to last_label (in PC order). */
+static memory_list labels_memlist;
 static int first_label, last_label;
-static int32 *label_offsets;       /* Double-linked list of label offsets    */
-static int   *label_next,          /* (i.e. zmachine_pc values) in PC order  */
-             *label_prev;
-static int32 *label_symbols;       /* Symbol numbers if defined in source    */
 
-static int   *sequence_point_labels;
-                                   /* Label numbers for each                 */
-static debug_location *sequence_point_locations;
-                                   /* Source code references for each        */
-                                   /* (used for making debugging file)       */
+static int *labeluse;     /* Flags indicating whether a given label has been
+                             used as a branch target yet. */
+static memory_list labeluse_memlist;
+static int labeluse_size; /* Entries up to here are initialized */
+
+static sequencepointinfo *sequence_points; /* Allocated to next_sequence_point.
+                                              Only used when debugfile_switch
+                                              is set.                        */
+static memory_list sequence_points_memlist;
+
+/* ------------------------------------------------------------------------- */
+/*   Label management                                                        */
+/* ------------------------------------------------------------------------- */
+
+/* The stripping of unreachable code requires a bit of explanation.
+
+   As we compile a function, we update the execution_never_reaches_here
+   flag to reflect whether the current line is reachable. If the flag
+   is set (EXECSTATE_UNREACHABLE), we skip generating opcodes,
+   compiling strings, and so on. (See assemblez_instruction(),
+   assembleg_instruction(), and compile_string() for these checks.)
+
+   If we're *between* functions, the execution_never_reaches_here flag
+   is always clear (EXECSTATE_REACHABLE), so global strings are
+   compiled correctly.
+
+   In general, every time we compile a JUMP or RETURN opcode, the flag
+   gets set. Every time we compile a label, the flag gets cleared.
+   This makes sense if you consider a common "while" loop:
+
+     while (true) {
+       ...
+       if (flag) break;
+       ...
+     }
+     ...
+
+   This is compiled as:
+
+     .TopLabel;
+     ...
+     @jnz flag ?ExitLabel;
+     ...
+     @jump TopLabel;
+     .ExitLabel;
+     ...
+   
+   Code after an unconditional JUMP is obviously unreachable. But the
+   next thing that happens is the .ExitLabel, which is the target of a
+   branch, so the next line is reachable again.
+
+   However, if the unreachable flag is set when we *begin* a statement
+   (or braced block of statements), we can get tougher. We set the
+   EXECSTATE_ENTIRE flag for the entire duration of the statement or
+   block. This flag cannot be cleared by compiling labels. An example
+   of this:
+
+     if (DOSTUFF) {
+       while (true) {
+         if (flag) break;
+       }
+     }
+
+   If the DOSTUFF constant is false, the entire "while" loop is definitely
+   unreachable. So we should skip .TopLabel, .ExitLabel, and everything
+   in between. To ensure this, we set EXECSTATE_ENTIRE upon entering the
+   "if {...}" braced block, and reset it upon leaving.
+
+   (See parse_code_block() and parse_statement() for the (slightly fugly)
+   bit-fidding that accomplishes this.)
+
+   As an added optimization, some labels are known to be "forward"; they
+   are only reached by forward jumps. (.ExitLabel above is an example.)
+   If we reach a forward label and nothing has in fact jumped there,
+   the label is dead and we can skip it. (And thus also skip clearing
+   the unreachable flag!)
+
+   To understand *that*, consider a "while true" loop with no "break":
+
+     while (true) {
+       ...
+       if (flag) return;
+       ...
+     }
+
+   This never branches to .ExitLabel. So when we reach .ExitLabel,
+   we can say for sure that *nothing* branches there. So we skip
+   compiling that label. The unreachable flag is left set (because we
+   just finished the jump to .TopLabel). Thus, any code following the
+   entire loop is known to be unreachable, and we can righteously
+   complain about it.
+
+   (In contrast, .TopLabel cannot be skipped because it might be the
+   target of a *backwards* branch later on. In fact there might be
+   several -- any "continue" in the loop will jump to .TopLabel.)
+*/
+
+/* Set the position of the given label. The offset will be the current
+   zmachine_pc, or -1 if the label is definitely unused.
 
+   This adds the label to a linked list (via first_label, last_label).
+
+   The linked list must be in increasing PC order. We know this will
+   be true because we call this as we run through the function, so
+   zmachine_pc always increases.
+*/
 static void set_label_offset(int label, int32 offset)
 {
-    if (label >= MAX_LABELS) memoryerror("MAX_LABELS", MAX_LABELS);
-
-    label_offsets[label] = offset;
+    ensure_memory_list_available(&labels_memlist, label+1);
+
+    labels[label].offset = offset;
+    labels[label].symbol = -1;
+    if (offset < 0) {
+        /* Mark this label as invalid and don't put it in the linked list. */
+        labels[label].prev = -1;
+        labels[label].next = -1;
+        return;
+    }
+    
     if (last_label == -1)
-    {   label_prev[label] = -1;
+    {   labels[label].prev = -1;
         first_label = label;
     }
     else
-    {   label_prev[label] = last_label;
-        label_next[last_label] = label;
+    {   labels[label].prev = last_label;
+        labels[last_label].next = label;
     }
     last_label = label;
-    label_next[label] = -1;
-    label_symbols[label] = -1;
+    labels[label].next = -1;
+}
+
+/* Set a flag indicating that the given label has been jumped to. */
+static void mark_label_used(int label)
+{
+    if (label < 0)
+        return;
+
+    /* Entries from 0 to labeluse_size have meaningful values.
+       If we have to increase labeluse_size, initialize the new
+       entries to FALSE. */
+    ensure_memory_list_available(&labeluse_memlist, label+1);
+    for (; labeluse_size < label+1; labeluse_size++) {
+        labeluse[labeluse_size] = FALSE;
+    }
+    labeluse[label] = TRUE;
 }
 
 /* ------------------------------------------------------------------------- */
@@ -187,7 +318,7 @@ extern int is_variable_ot(int otval)
 extern char *variable_name(int32 i)
 {
     if (i==0) return("sp");
-    if (i<MAX_LOCAL_VARIABLES) return local_variable_texts[i-1];
+    if (i<MAX_LOCAL_VARIABLES) return local_variable_names[i-1].text;
 
     if (!glulx_mode) {
       if (i==255) return("TEMP1");
@@ -218,25 +349,57 @@ extern char *variable_name(int32 i)
       }
     }
 
-    return ((char *) symbs[variable_tokens[i]]);
+    return (symbols[variables[i].token].name);
 }
 
-static void print_operand_z(assembly_operand o)
-{   switch(o.type)
+/* Print symbolic information about the AO, if there is any. */
+static void print_operand_annotation(const assembly_operand *o)
+{
+    int any = FALSE;
+    if (o->marker) {
+        printf((!any) ? " (" : ": ");
+        any = TRUE;
+        printf("%s", describe_mv(o->marker));
+        switch (o->marker) {
+        case VROUTINE_MV:
+            printf(": %s", veneer_routine_name(o->value));
+            break;
+        case INCON_MV:
+            printf(": %s", name_of_system_constant(o->value));
+            break;
+        case DWORD_MV:
+            printf(": '");
+            print_dict_word(o->value);
+            printf("'");
+            break;
+        }
+    }
+    if (o->symindex >= 0 && o->symindex < no_symbols) {
+        printf((!any) ? " (" : ": ");
+        any = TRUE;
+        printf("%s", symbols[o->symindex].name);
+    }
+    if (any) printf(")");       
+}
+
+static void print_operand_z(const assembly_operand *o, int annotate)
+{   switch(o->type)
     {   case EXPRESSION_OT: printf("expr_"); break;
         case LONG_CONSTANT_OT: printf("long_"); break;
         case SHORT_CONSTANT_OT: printf("short_"); break;
         case VARIABLE_OT:
-             if (o.value==0) { printf("sp"); return; }
-             printf("%s", variable_name(o.value)); return;
+             if (o->value==0) { printf("sp"); return; }
+             printf("%s", variable_name(o->value)); return;
         case OMITTED_OT: printf("<no value>"); return;
     }
-    printf("%d", o.value);
+    printf("%d", o->value);
+    if (annotate)
+        print_operand_annotation(o);
 }
 
-static void print_operand_g(assembly_operand o)
+static void print_operand_g(const assembly_operand *o, int annotate)
 {
-  switch (o.type) {
+  switch (o->type) {
   case EXPRESSION_OT: printf("expr_"); break;
   case CONSTANT_OT: printf("long_"); break;
   case HALFCONSTANT_OT: printf("short_"); break;
@@ -244,32 +407,34 @@ static void print_operand_g(assembly_operand o)
   case ZEROCONSTANT_OT: printf("zero_"); return;
   case DEREFERENCE_OT: printf("*"); break;
   case GLOBALVAR_OT: 
-    printf("%s (global_%d)", variable_name(o.value), o.value); 
+    printf("global_%d (%s)", o->value, variable_name(o->value)); 
     return;
   case LOCALVAR_OT: 
-    if (o.value == 0)
+    if (o->value == 0)
       printf("stackptr"); 
     else
-      printf("%s (local_%d)", variable_name(o.value), o.value-1); 
+      printf("local_%d (%s)", o->value-1, variable_name(o->value)); 
     return;
   case SYSFUN_OT:
-    if (o.value >= 0 && o.value < NUMBER_SYSTEM_FUNCTIONS)
-      printf("%s", system_functions.keywords[o.value]);
+    if (o->value >= 0 && o->value < NUMBER_SYSTEM_FUNCTIONS)
+      printf("%s", system_functions.keywords[o->value]);
     else
       printf("<unnamed system function>");
     return;
   case OMITTED_OT: printf("<no value>"); return;
   default: printf("???_"); break; 
   }
-  printf("%d", o.value);
+  printf("%d", o->value);
+  if (annotate)
+    print_operand_annotation(o);
 }
 
-extern void print_operand(assembly_operand o)
+extern void print_operand(const assembly_operand *o, int annotate)
 {
   if (!glulx_mode)
-    print_operand_z(o);
+    print_operand_z(o, annotate);
   else
-    print_operand_g(o);
+    print_operand_g(o, annotate);
 }
 
 /* ------------------------------------------------------------------------- */
@@ -277,8 +442,9 @@ extern void print_operand(assembly_operand o)
 /* ------------------------------------------------------------------------- */
 
 static void byteout(int32 i, int mv)
-{   if (zcode_ha_size >= MAX_ZCODE_SIZE)
-        memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE);
+{
+    ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+1);
+    ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+1);
     zcode_markers[zcode_ha_size] = (uchar) mv;
     zcode_holding_area[zcode_ha_size++] = (uchar) i;
     zmachine_pc++;
@@ -286,7 +452,7 @@ static void byteout(int32 i, int mv)
 
 /* ------------------------------------------------------------------------- */
 /*   A database of the 115 canonical Infocom opcodes in Versions 3 to 6      */
-/*   And of the however-many-there-are Glulx opcode                          */
+/*   And of the however-many-there-are Glulx opcodes                         */
 /* ------------------------------------------------------------------------- */
 
 typedef struct opcodez
@@ -337,6 +503,7 @@ typedef struct opcodeg
 #define GOP_MemHeap      2   /* uses_memheap_features */
 #define GOP_Acceleration 4   /* uses_acceleration_features */
 #define GOP_Float        8   /* uses_float_features */
+#define GOP_ExtUndo     16   /* uses_extundo_features */
 
     /* Codes for the number of operands */
 
@@ -634,6 +801,8 @@ static opcodeg opcodes_table_g[] = {
   { (uchar *) "jfge",       0x1C5,  Br, GOP_Float, 3 },
   { (uchar *) "jisnan",     0x1C8,  Br, GOP_Float, 2 },
   { (uchar *) "jisinf",     0x1C9,  Br, GOP_Float, 2 },
+  { (uchar *) "hasundo",    0x128,  St, GOP_ExtUndo, 1 },
+  { (uchar *) "discardundo",0x129,   0, GOP_ExtUndo, 0 },
 };
 
 /* The opmacros table is used for fake opcodes. The opcode numbers are
@@ -792,9 +961,10 @@ static void write_operand(assembly_operand op)
     }
 }
 
-extern void assemblez_instruction(assembly_instruction *AI)
+extern void assemblez_instruction(const assembly_instruction *AI)
 {
-    uchar *start_pc, *operands_pc;
+    int32 operands_pc;
+    int32 start_pc;
     int32 offset, j, topbits=0, types_byte1, types_byte2;
     int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE;
     assembly_operand o1, o2;
@@ -802,6 +972,15 @@ extern void assemblez_instruction(assembly_instruction *AI)
 
     ASSERT_ZCODE();
 
+    if (execution_never_reaches_here) {
+        if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
+            warning("This statement can never be reached");
+            /* only show the warning once */
+            execution_never_reaches_here |= EXECSTATE_NOWARN;
+        }
+        return;
+    }
+
     offset = zmachine_pc;
 
     no_instructions++;
@@ -810,8 +989,10 @@ extern void assemblez_instruction(assembly_instruction *AI)
     if (sequence_point_follows)
     {   sequence_point_follows = FALSE; at_seq_point = TRUE;
         if (debugfile_switch)
-        {   sequence_point_labels[next_sequence_point] = next_label;
-            sequence_point_locations[next_sequence_point] =
+        {
+            ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
+            sequence_points[next_sequence_point].label = next_label;
+            sequence_points[next_sequence_point].location =
                 statement_debug_location;
             set_label_offset(next_label++, zmachine_pc);
         }
@@ -825,11 +1006,8 @@ extern void assemblez_instruction(assembly_instruction *AI)
         return;
     }
 
-    if (execution_never_reaches_here)
-        warning("This statement can never be reached");
-
     operand_rules = opco.op_rules;
-    execution_never_reaches_here = ((opco.flags & Rf) != 0);
+    execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
 
     if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1;
 
@@ -840,7 +1018,7 @@ extern void assemblez_instruction(assembly_instruction *AI)
 
     /* 1. Write the opcode byte(s) */
 
-    start_pc = zcode_holding_area + zcode_ha_size;
+    start_pc = zcode_ha_size;
 
     switch(opco.no)
     {   case VAR_LONG: topbits=0xc0; min=0; max=8; break;
@@ -855,23 +1033,31 @@ extern void assemblez_instruction(assembly_instruction *AI)
     }
     byteout(opco.code + topbits, 0);
 
-    operands_pc = zcode_holding_area + zcode_ha_size;
+    operands_pc = zcode_ha_size;
 
     /* 2. Dispose of the special rules LABEL and TEXT */
 
     if (operand_rules==LABEL)
     {   j = (AI->operand[0]).value;
+        mark_label_used(j);
         byteout(j/256, LABEL_MV); byteout(j%256, 0);
         goto Instruction_Done;
     }
 
     if (operand_rules==TEXT)
     {   int32 i;
-        uchar *tmp = translate_text(zcode_holding_area + zcode_ha_size, zcode_holding_area+MAX_ZCODE_SIZE, AI->text, STRCTX_GAMEOPC);
-        if (!tmp)
-            memoryerror("MAX_ZCODE_SIZE", MAX_ZCODE_SIZE);
-        j = subtract_pointers(tmp, (zcode_holding_area + zcode_ha_size));
-        for (i=0; i<j; i++) zcode_markers[zcode_ha_size++] = 0;
+        j = translate_text(-1, AI->text, STRCTX_GAMEOPC);
+        if (j < 0) {
+            error("text translation failed");
+            j = 0;
+        }
+        ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+j);
+        ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+j);
+        for (i=0; i<j; i++) {
+            zcode_holding_area[zcode_ha_size] = translated_text[i];
+            zcode_markers[zcode_ha_size] = 0;
+            zcode_ha_size++;
+        }
         zmachine_pc += j;
         goto Instruction_Done;
     }
@@ -902,13 +1088,13 @@ extern void assemblez_instruction(assembly_instruction *AI)
                 else
                     types_byte2 = (types_byte2 & (~mask)) + o1.type*multi;
             }
-            *operands_pc=types_byte1;
-            if (opco.no == VAR_LONG) *(operands_pc+1)=types_byte2;
+            zcode_holding_area[operands_pc]=types_byte1;
+            if (opco.no == VAR_LONG) zcode_holding_area[operands_pc+1]=types_byte2;
             break;
 
         case ONE:
             o1 = AI->operand[0];
-            *start_pc=(*start_pc) + o1.type*0x10;
+            zcode_holding_area[start_pc] += o1.type*0x10;
             write_operand(o1);
             break;
 
@@ -919,12 +1105,12 @@ extern void assemblez_instruction(assembly_instruction *AI)
             /* Transfer to VAR form if either operand is a long constant */
 
             if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT))
-            {   *start_pc=(*start_pc) + 0xc0;
+            {   zcode_holding_area[start_pc] += 0xc0;
                 byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0);
             }
             else
-            {   if (o1.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x40;
-                if (o2.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x20;
+            {   if (o1.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x40;
+                if (o2.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x20;
             }
             write_operand(o1);
             write_operand(o2);
@@ -934,12 +1120,12 @@ extern void assemblez_instruction(assembly_instruction *AI)
     /* 4. Assemble a Store destination, if needed */
 
     if ((AI->store_variable_number) != -1)
-    {   if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES) {
+    {   if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_ZCODE_GLOBAL_VARS) {
             goto OpcodeSyntaxError;
         }
         o1.type = VARIABLE_OT;
         o1.value = AI->store_variable_number;
-        variable_usage[o1.value] = TRUE;
+        variables[o1.value].usage = TRUE;
         o1.marker = 0;
 
         /*  Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
@@ -956,7 +1142,7 @@ extern void assemblez_instruction(assembly_instruction *AI)
     if (AI->branch_label_number != -1)
     {   int32 addr, long_form;
         int branch_on_true = (AI->branch_flag)?1:0;
-
+        mark_label_used(AI->branch_label_number);
         switch (AI->branch_label_number)
         {   case -2: addr = 2; branch_on_true = 0; long_form = 0; break;
                                                  /* branch nowhere, carry on */
@@ -994,7 +1180,7 @@ extern void assemblez_instruction(assembly_instruction *AI)
         for (i=0; i<AI->operand_count; i++)
         {   if ((i==0) && (opco.op_rules == VARIAB))
             {   if ((AI->operand[0]).type == VARIABLE_OT)
-                {   printf("["); print_operand_z(AI->operand[i]); }
+                {   printf("["); print_operand_z(&AI->operand[i], TRUE); }
                 else
                     printf("%s", variable_name((AI->operand[0]).value));
             }
@@ -1002,14 +1188,14 @@ extern void assemblez_instruction(assembly_instruction *AI)
             if ((i==0) && (opco.op_rules == LABEL))
             {   printf("L%d", AI->operand[0].value);
             }
-            else print_operand_z(AI->operand[i]);
+            else print_operand_z(&AI->operand[i], TRUE);
             printf(" ");
         }
         if (AI->store_variable_number != -1)
         {   assembly_operand AO;
             printf("-> ");
             AO.type = VARIABLE_OT; AO.value = AI->store_variable_number;
-            print_operand_z(AO); printf(" ");
+            print_operand_z(&AO, TRUE); printf(" ");
         }
 
         switch(AI->branch_label_number)
@@ -1025,10 +1211,10 @@ extern void assemblez_instruction(assembly_instruction *AI)
         }
 
         if (asm_trace_level>=2)
-        {   for (j=0;start_pc<zcode_holding_area + zcode_ha_size;
+        {   for (j=0;start_pc<zcode_ha_size;
                  j++, start_pc++)
             {   if (j%16==0) printf("\n                               ");
-                printf("%02x ", *start_pc);
+                printf("%02x ", zcode_holding_area[start_pc]);
             }
         }
         printf("\n");
@@ -1044,7 +1230,7 @@ extern void assemblez_instruction(assembly_instruction *AI)
     error_named("Assembly mistake: syntax is", opcode_syntax_string);
 }
 
-static void assembleg_macro(assembly_instruction *AI)
+static void assembleg_macro(const assembly_instruction *AI)
 {
     /* validate macro syntax first */
     int ix, no_operands_given;
@@ -1098,9 +1284,10 @@ static void assembleg_macro(assembly_instruction *AI)
     error_named("Assembly mistake: syntax is", opcode_syntax_string);
 }
 
-extern void assembleg_instruction(assembly_instruction *AI)
+extern void assembleg_instruction(const assembly_instruction *AI)
 {
-    uchar *start_pc, *opmodes_pc;
+    int32 opmodes_pc;
+    int32 start_pc;
     int32 offset, j;
     int no_operands_given, at_seq_point = FALSE;
     int ix, k;
@@ -1108,6 +1295,15 @@ extern void assembleg_instruction(assembly_instruction *AI)
 
     ASSERT_GLULX();
 
+    if (execution_never_reaches_here) {
+        if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
+            warning("This statement can never be reached");
+            /* only show the warning once */
+            execution_never_reaches_here |= EXECSTATE_NOWARN;
+        }
+        return;
+    }
+
     offset = zmachine_pc;
 
     no_instructions++;
@@ -1116,8 +1312,10 @@ extern void assembleg_instruction(assembly_instruction *AI)
     if (sequence_point_follows)
     {   sequence_point_follows = FALSE; at_seq_point = TRUE;
         if (debugfile_switch)
-        {   sequence_point_labels[next_sequence_point] = next_label;
-            sequence_point_locations[next_sequence_point] =
+        {
+            ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
+            sequence_points[next_sequence_point].label = next_label;
+            sequence_points[next_sequence_point].location =
                 statement_debug_location;
             set_label_offset(next_label++, zmachine_pc);
         }
@@ -1126,10 +1324,7 @@ extern void assembleg_instruction(assembly_instruction *AI)
 
     opco = internal_number_to_opcode_g(AI->internal_number);
 
-    if (execution_never_reaches_here)
-        warning("This statement can never be reached");
-
-    execution_never_reaches_here = ((opco.flags & Rf) != 0);
+    execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
 
     if (opco.op_rules & GOP_Unicode) {
         uses_unicode_features = TRUE;
@@ -1143,12 +1338,15 @@ extern void assembleg_instruction(assembly_instruction *AI)
     if (opco.op_rules & GOP_Float) {
         uses_float_features = TRUE;
     }
+    if (opco.op_rules & GOP_ExtUndo) {
+        uses_extundo_features = TRUE;
+    }
 
     no_operands_given = AI->operand_count;
 
     /* 1. Write the opcode byte(s) */
 
-    start_pc = zcode_holding_area + zcode_ha_size; 
+    start_pc = zcode_ha_size; 
 
     if (opco.code < 0x80) {
       byteout(opco.code, 0);
@@ -1168,7 +1366,7 @@ extern void assembleg_instruction(assembly_instruction *AI)
        every two operands (rounded up). We write zeroes for now; 
        when the operands are written, we'll go back and fix them. */
 
-    opmodes_pc = zcode_holding_area + zcode_ha_size;
+    opmodes_pc = zcode_ha_size;
 
     for (ix=0; ix<opco.no; ix+=2) {
       byteout(0, 0);
@@ -1193,6 +1391,7 @@ extern void assembleg_instruction(assembly_instruction *AI)
                 compiler_error("Assembling branch without BRANCH_MV marker");
                 goto OpcodeSyntaxError; 
             }
+            mark_label_used(k);
             if (k == -2) {
                 k = 2; /* branch no-op */
                 type = BYTECONSTANT_OT;
@@ -1210,8 +1409,7 @@ extern void assembleg_instruction(assembly_instruction *AI)
             }
             else {
                 /* branch to label k */
-                j = subtract_pointers((zcode_holding_area + zcode_ha_size), 
-                    opmodes_pc);
+                j = (zcode_ha_size - opmodes_pc);
                 j = 2*j - ix;
                 marker = BRANCH_MV + j;
                 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
@@ -1340,7 +1538,7 @@ extern void assembleg_instruction(assembly_instruction *AI)
 
       if (ix & 1)
           j = (j << 4);
-      opmodes_pc[ix/2] |= j;
+      zcode_holding_area[opmodes_pc+ix/2] |= j;
     }
 
     /* Print assembly trace. */
@@ -1359,23 +1557,23 @@ extern void assembleg_instruction(assembly_instruction *AI)
                 printf("to L%d", AI->operand[i].value);
             }
           else {
-            print_operand_g(AI->operand[i]);
+            print_operand_g(&AI->operand[i], TRUE);
           }
           printf(" ");
       }
 
       if (asm_trace_level>=2) {
         for (j=0;
-            start_pc<zcode_holding_area + zcode_ha_size;
+            start_pc<zcode_ha_size;
             j++, start_pc++) {
             if (j%16==0) printf("\n                               ");
             if (/* DISABLES CODE */ (0)) {
-                printf("%02x ", *start_pc);
+                printf("%02x ", zcode_holding_area[start_pc]);
             }
             else {
-                printf("%02x", *start_pc);
-                if (zcode_markers[start_pc-zcode_holding_area])
-                    printf("{%02x}", zcode_markers[start_pc-zcode_holding_area]);
+                printf("%02x", zcode_holding_area[start_pc]);
+                if (zcode_markers[start_pc])
+                    printf("{%02x}", zcode_markers[start_pc]);
                 printf(" ");
             }
         }
@@ -1393,17 +1591,57 @@ extern void assembleg_instruction(assembly_instruction *AI)
     error_named("Assembly mistake: syntax is", opcode_syntax_string);
 }
 
+/* Set up this label at zmachine_pc.
+   This resets the execution_never_reaches_here flag, since every label
+   is assumed to be reachable. 
+   However, if STRIP_UNREACHABLE_LABELS and EXECSTATE_ENTIRE are both set,
+   that's not true. The entire statement is being skipped, so we can safely
+   skip all labels within it.
+   (If STRIP_UNREACHABLE_LABELS is not set, the ENTIRE flag is ignored.)
+*/
 extern void assemble_label_no(int n)
 {
+    if ((execution_never_reaches_here & EXECSTATE_ENTIRE) && STRIP_UNREACHABLE_LABELS) {
+        /* We're not going to compile this label at all. Set a negative
+           offset, which will trip an error if this label is jumped to. */
+        set_label_offset(n, -1);
+        return;
+    }
+
     if (asm_trace_level > 0)
         printf("%5d  +%05lx    .L%d\n", ErrorReport.line_number,
             ((long int) zmachine_pc), n);
     set_label_offset(n, zmachine_pc);
-    execution_never_reaches_here = FALSE;
+    execution_never_reaches_here = EXECSTATE_REACHABLE;
+}
+
+/* This is the same as assemble_label_no, except we only set up the label
+   if there has been a forward branch to it.
+   Returns whether the label is created.
+   Only use this for labels which never have backwards branches!
+*/
+extern int assemble_forward_label_no(int n)
+{
+    if (n >= 0 && n < labeluse_size && labeluse[n]) {
+        assemble_label_no(n);
+        return TRUE;
+    }
+    else {
+        /* There were no forward branches to this label and we promise
+           there will be no backwards branches to it. Set a negative
+           offset, which will trip an error if we break our promise. */
+        set_label_offset(n, -1);
+        return FALSE;
+    }
 }
 
 extern void define_symbol_label(int symbol)
-{   label_symbols[svals[symbol]] = symbol;
+{
+    int label = symbols[symbol].value;
+    /* We may be creating a new label (label = next_label) or filling in
+       the value of an old one. So we call ensure. */
+    ensure_memory_list_available(&labels_memlist, label+1);
+    labels[label].symbol = symbol;
 }
 
 extern int32 assemble_routine_header(int no_locals,
@@ -1412,13 +1650,15 @@ extern int32 assemble_routine_header(int no_locals,
     int stackargs = FALSE;
     int name_length;
 
-    execution_never_reaches_here = FALSE;
+    execution_never_reaches_here = EXECSTATE_REACHABLE;
 
     routine_locals = no_locals;
-    for (i=0; i<MAX_LOCAL_VARIABLES; i++) variable_usage[i] = FALSE;
+    
+    ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES);
+    for (i=0; i<MAX_LOCAL_VARIABLES; i++) variables[i].usage = FALSE;
 
-    if (no_locals >= 1 
-      && !strcmp(local_variables.keywords[0], "_vararg_count")) {
+    if (no_locals >= 1
+      && strcmpcis(local_variable_names[0].text, "_vararg_count")==0) {
       stackargs = TRUE;
     }
 
@@ -1448,9 +1688,8 @@ extern int32 assemble_routine_header(int no_locals,
 
     routine_symbol = the_symbol;
     name_length = strlen(name) + 1;
-    routine_name =
-      my_malloc(name_length * sizeof(char), "temporary copy of routine name");
-    strncpy(routine_name, name, name_length);
+    ensure_memory_list_available(&current_routine_name, name_length);
+    strncpy(current_routine_name.data, name, name_length);
 
     /*  Update the routine counter                                           */
 
@@ -1476,6 +1715,7 @@ extern int32 assemble_routine_header(int no_locals,
           for (i=0; i<no_locals; i++) { byteout(0,0); byteout(0,0); }
 
       next_label = 0; next_sequence_point = 0; last_label = -1;
+      labeluse_size = 0;
 
       /*  Compile code to print out text like "a=3, b=4, c=5" when the       */
       /*  function is called, if it's required.                              */
@@ -1495,7 +1735,8 @@ extern int32 assemble_routine_header(int no_locals,
             }
             else
             {   i = no_named_routines++;
-                  named_routine_symbols[i] = the_symbol;
+                ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
+                named_routine_symbols[i] = the_symbol;
                 CON.value = i/8; CON.type = LONG_CONSTANT_OT; CON.marker = 0;
                 RFA.value = routine_flags_array_SC;
                 RFA.type = LONG_CONSTANT_OT; RFA.marker = INCON_MV;
@@ -1522,6 +1763,7 @@ extern int32 assemble_routine_header(int no_locals,
         assemble_label_no(ln);
         sprintf(fnt, ") ]^"); AI.text = fnt;
         assemblez_0(print_zc);
+        AI.text = NULL;
         assemble_label_no(ln2);
       }
 
@@ -1558,6 +1800,7 @@ extern int32 assemble_routine_header(int no_locals,
       }
 
       next_label = 0; next_sequence_point = 0; last_label = -1; 
+      labeluse_size = 0;
 
       if ((routine_asterisked) || (define_INFIX_switch)) {
         int ix;
@@ -1569,6 +1812,7 @@ extern int32 assemble_routine_header(int no_locals,
           }
           else {
             i = no_named_routines++;
+            ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
             named_routine_symbols[i] = the_symbol;
           }
         }
@@ -1679,25 +1923,26 @@ void assemble_routine_end(int embedded_flag, debug_locations locations)
 
     if (debugfile_switch)
     {
+        char *routine_name = current_routine_name.data;
         debug_file_printf("<routine>");
         if (embedded_flag)
         {   debug_file_printf
                 ("<identifier artificial=\"true\">%s</identifier>",
                  routine_name);
         }
-        else if (sflags[routine_symbol] & REPLACE_SFLAG)
+        else if (symbols[routine_symbol].flags & REPLACE_SFLAG)
         {   /* The symbol type will be set to ROUTINE_T once the replaced
                version has been given; if it is already set, we must be dealing
                with a replacement, and we can use the routine name as-is.
                Otherwise we look for a rename.  And if that doesn't work, we
                fall back to an artificial identifier. */
-            if (stypes[routine_symbol] == ROUTINE_T)
+            if (symbols[routine_symbol].type == ROUTINE_T)
             {   /* Optional because there may be further replacements. */
                 write_debug_optional_identifier(routine_symbol);
             }
             else if (find_symbol_replacement(&routine_symbol))
             {   debug_file_printf
-                    ("<identifier>%s</identifier>", symbs[routine_symbol]);
+                    ("<identifier>%s</identifier>", symbols[routine_symbol].name);
             }
             else
             {   debug_file_printf
@@ -1737,67 +1982,61 @@ void assemble_routine_end(int embedded_flag, debug_locations locations)
         {   debug_file_printf("<sequence-point>");
             debug_file_printf("<address>");
             write_debug_code_backpatch
-                (label_offsets[sequence_point_labels[i]]);
+                (labels[sequence_points[i].label].offset);
             debug_file_printf("</address>");
-            write_debug_location(sequence_point_locations[i]);
+            write_debug_location(sequence_points[i].location);
             debug_file_printf("</sequence-point>");
         }
         debug_file_printf("</routine>");
     }
 
-    my_free(&routine_name, "temporary copy of routine name");
-
     /* Issue warnings about any local variables not used in the routine. */
 
     for (i=1; i<=routine_locals; i++)
-        if (!(variable_usage[i]))
+        if (!(variables[i].usage))
             dbnu_warning("Local variable", variable_name(i),
                 routine_starts_line);
 
     for (i=0; i<next_label; i++)
-    {   int j = label_symbols[i];
+    {   int j = labels[i].symbol;
         if (j != -1)
-        {   if (sflags[j] & CHANGE_SFLAG)
+        {   if (symbols[j].flags & CHANGE_SFLAG)
                 error_named_at("Routine contains no such label as",
-                    (char *) symbs[j], slines[j]);
+                    symbols[j].name, symbols[j].line);
             else
-                if ((sflags[j] & USED_SFLAG) == 0)
-                    dbnu_warning("Label", (char *) symbs[j], slines[j]);
-            stypes[j] = CONSTANT_T;
-            sflags[j] = UNKNOWN_SFLAG;
+                if ((symbols[j].flags & USED_SFLAG) == 0)
+                    dbnu_warning("Label", symbols[j].name, symbols[j].line);
+            symbols[j].type = CONSTANT_T;
+            symbols[j].flags = UNKNOWN_SFLAG;
         }
     }
     no_sequence_points += next_sequence_point;
     next_label = 0; next_sequence_point = 0;
+    labeluse_size = 0;
+    execution_never_reaches_here = EXECSTATE_REACHABLE;
 }
 
 /* ------------------------------------------------------------------------- */
 /*   Called when the holding area contains an entire routine of code:        */
 /*   backpatches the labels, issues module markers, then dumps the routine   */
 /*   into longer-term storage.                                               */
+/*                                                                           */
 /*   Note that in the code received, all branches have long form, and their  */
 /*   contents are not an offset but the label numbers they branch to.        */
 /*   Similarly, LABEL operands (those of "jump" instructions) are label      */
 /*   numbers.  So this routine must change the label numbers to offsets,     */
 /*   slimming the code down as it does so to take advantage of short-form    */
 /*   branch operands where possible.                                         */
+/*                                                                           */
+/*   zcode_ha_size is the number of bytes added since the last transfer      */
+/*   call. So we transfer starting at (zmachine_pc - zcode_ha_size). But we  */
+/*   might transfer fewer bytes than that.                                   */
 /* ------------------------------------------------------------------------- */
 
-static int32 adjusted_pc;
-
-static void transfer_to_temp_file(uchar *c)
-{   fputc(*c,Temp2_fp);
-    adjusted_pc++;
-}
-
-static void transfer_to_zcode_area(uchar *c)
-{   write_byte_to_memory_block(&zcode_area, adjusted_pc++, *c);
-}
-
 static void transfer_routine_z(void)
 {   int32 i, j, pc, new_pc, label, long_form, offset_of_next, addr,
           branch_on_true, rstart_pc;
-    void (* transfer_byte)(uchar *);
+    int32 adjusted_pc;
 
     adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
 
@@ -1806,12 +2045,12 @@ static void transfer_routine_z(void)
              (long int) adjusted_pc, zcode_ha_size, next_label);
     }
 
-    transfer_byte =
-        (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
-
     /*  (1) Scan through for branches and make short/long decisions in each
             case.  Mark omitted bytes (2nd bytes in branches converted to
-            short form) with DELETED_MV.                                     */
+            short form) with DELETED_MV.
+            We also look for jumps that can be entirely eliminated (because
+            they are jumping to the very next instruction). The opcode and
+            both label bytes get DELETED_MV. */
 
     for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++)
     {   if (zcode_markers[i] == BRANCH_MV)
@@ -1819,10 +2058,25 @@ static void transfer_routine_z(void)
                 printf("Branch detected at offset %04x\n", pc);
             j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
             if (asm_trace_level >= 4)
-                printf("To label %d, which is %d from here\n",
-                    j, label_offsets[j]-pc);
-            if ((label_offsets[j] >= pc+2) && (label_offsets[j] < pc+64))
-            {   if (asm_trace_level >= 4) printf("Short form\n");
+                printf("...To label %d, which is %d from here\n",
+                    j, labels[j].offset-pc);
+            if ((labels[j].offset >= pc+2) && (labels[j].offset < pc+64))
+            {   if (asm_trace_level >= 4) printf("...Using short form\n");
+                zcode_markers[i+1] = DELETED_MV;
+            }
+        }
+        else if (zcode_markers[i] == LABEL_MV)
+        {
+            if (asm_trace_level >= 4)
+                printf("Jump detected at offset %04x\n", pc);
+            j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
+            if (asm_trace_level >= 4)
+                printf("...To label %d, which is %d from here\n",
+                    j, labels[j].offset-pc);
+            if (labels[j].offset-pc == 2 && i >= 1 && zcode_holding_area[i-1] == opcodes_table_z[jump_zc].code+128) {
+                if (asm_trace_level >= 4) printf("...Deleting jump\n");
+                zcode_markers[i-1] = DELETED_MV;
+                zcode_markers[i] = DELETED_MV;
                 zcode_markers[i+1] = DELETED_MV;
             }
         }
@@ -1841,17 +2095,18 @@ static void transfer_routine_z(void)
         {   printf("Opening label: %d\n", first_label);
             for (i=0;i<next_label;i++)
                 printf("Label %d offset %04x next -> %d previous -> %d\n",
-                    i, label_offsets[i], label_next[i], label_prev[i]);
+                    i, labels[i].offset, labels[i].next, labels[i].prev);
         }
 
+        /* label will advance through the linked list as pc increases. */
         for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
             i<zcode_ha_size; i++, pc++)
-        {   while ((label != -1) && (label_offsets[label] == pc))
+        {   while ((label != -1) && (labels[label].offset == pc))
             {   if (asm_trace_level >= 4)
                     printf("Position of L%d corrected from %04x to %04x\n",
-                        label, label_offsets[label], new_pc);
-                label_offsets[label] = new_pc;
-                label = label_next[label];
+                        label, labels[label].offset, new_pc);
+                labels[label].offset = new_pc;
+                label = labels[label].next;
             }
            if (zcode_markers[i] != DELETED_MV) new_pc++;
         }
@@ -1861,6 +2116,8 @@ static void transfer_routine_z(void)
             operands with offsets to those labels.  Also issue markers, now
             that we know where they occur in the final Z-code area.          */
 
+    ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
+    
     for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++)
     {   switch(zcode_markers[i])
         { case BRANCH_MV:
@@ -1870,7 +2127,13 @@ static void transfer_routine_z(void)
             branch_on_true = ((zcode_holding_area[i]) & 0x80);
             offset_of_next = new_pc + long_form + 1;
 
-            addr = label_offsets[j] - offset_of_next + 2;
+            if (labels[j].offset < 0) {
+                error("Attempt to jump to an unreachable label");
+                addr = 0;
+            }
+            else {
+                addr = labels[j].offset - offset_of_next + 2;
+            }
             if (addr<-0x2000 || addr>0x1fff) 
                 fatalerror("Branch out of range: divide the routine up?");
             if (addr<0) addr+=(int32) 0x10000L;
@@ -1887,18 +2150,24 @@ static void transfer_routine_z(void)
                 }
                 zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f);
             }
-            transfer_byte(zcode_holding_area + i); new_pc++;
+            zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
             break;
 
           case LABEL_MV:
             j = 256*zcode_holding_area[i] + zcode_holding_area[i+1];
-            addr = label_offsets[j] - new_pc;
+            if (labels[j].offset < 0) {
+                error("Attempt to jump to an unreachable label");
+                addr = 0;
+            }
+            else {
+                addr = labels[j].offset - new_pc;
+            }
             if (addr<-0x8000 || addr>0x7fff) 
                 fatalerror("Jump out of range: divide the routine up?");
             if (addr<0) addr += (int32) 0x10000L;
             zcode_holding_area[i] = addr/256;
             zcode_holding_area[i+1] = addr%256;
-            transfer_byte(zcode_holding_area + i); new_pc++;
+            zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
             break;
 
           case DELETED_MV:
@@ -1920,20 +2189,26 @@ static void transfer_routine_z(void)
                         break;
                     }
 
-                    write_byte_to_memory_block(&zcode_backpatch_table,
-                        zcode_backpatch_size++,
-                        zcode_markers[i] + 32*(new_pc/65536));
-                    write_byte_to_memory_block(&zcode_backpatch_table,
-                        zcode_backpatch_size++, (new_pc/256)%256);
-                    write_byte_to_memory_block(&zcode_backpatch_table,
-                        zcode_backpatch_size++, new_pc%256);
+                    if (bpatch_trace_setting >= 2)
+                        printf("BP added: MV %d PC %04x\n", zcode_markers[i], new_pc);
+
+                    ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+3);
+                    zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i] + 32*(new_pc/65536);
+                    zcode_backpatch_table[zcode_backpatch_size++] = (new_pc/256)%256;
+                    zcode_backpatch_table[zcode_backpatch_size++] = new_pc%256;
                     break;
             }
-            transfer_byte(zcode_holding_area + i); new_pc++;
+            zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
             break;
         }
     }
 
+    /* Consistency check */
+    if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
+    {
+        fatalerror("Optimisation increased routine length or failed to match; should not happen");
+    }
+
     if (asm_trace_level >= 3)
     {   printf("After branch optimisation, routine length is %d bytes\n",
              new_pc - rstart_pc);
@@ -1942,13 +2217,12 @@ static void transfer_routine_z(void)
     /*  Insert null bytes if necessary to ensure the next routine address is */
     /*  expressible as a packed address                                      */
 
-    {   uchar zero[1];
-        zero[0] = 0;
-        if (oddeven_packing_switch)
-            while ((adjusted_pc%(scale_factor*2))!=0) transfer_byte(zero);
-        else
-            while ((adjusted_pc%scale_factor)!=0) transfer_byte(zero);
-    }
+    ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+2*scale_factor);
+
+    if (oddeven_packing_switch)
+        while ((adjusted_pc%(scale_factor*2))!=0) zcode_area[adjusted_pc++] = 0;
+    else
+        while ((adjusted_pc%scale_factor)!=0) zcode_area[adjusted_pc++] = 0;
 
     zmachine_pc = adjusted_pc;
     zcode_ha_size = 0;
@@ -1957,7 +2231,7 @@ static void transfer_routine_z(void)
 static void transfer_routine_g(void)
 {   int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr,
           rstart_pc;
-    void (* transfer_byte)(uchar *);
+    int32 adjusted_pc;
 
     adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
 
@@ -1966,12 +2240,12 @@ static void transfer_routine_g(void)
              (long int) adjusted_pc, zcode_ha_size, next_label);
     }
 
-    transfer_byte =
-        (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
-
     /*  (1) Scan through for branches and make short/long decisions in each
             case.  Mark omitted bytes (bytes 2-4 in branches converted to
-            short form) with DELETED_MV.                                     */
+            short form) with DELETED_MV.
+            We also look for branches that can be entirely eliminated (because
+            they are jumping to the very next instruction). The opcode and
+            all label bytes get DELETED_MV. */
 
     for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++) {
       if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
@@ -1984,16 +2258,25 @@ static void transfer_routine_g(void)
             | (zcode_holding_area[i+2] << 8)
             | (zcode_holding_area[i+3]));
         offset_of_next = pc + 4;
-        addr = (label_offsets[j] - offset_of_next) + 2;
+        addr = (labels[j].offset - offset_of_next) + 2;
+        opmodebyte = i - ((opmodeoffset+1)/2);
         if (asm_trace_level >= 4)
-            printf("To label %d, which is (%d-2) = %d from here\n",
-                j, addr, label_offsets[j] - offset_of_next);
-        if (addr >= -0x80 && addr < 0x80) {
+            printf("...To label %d, which is (%d-2) = %d from here\n",
+                j, addr, labels[j].offset - offset_of_next);
+        if (addr == 2 && i >= 2 && opmodeoffset == 2 && zcode_holding_area[opmodebyte-1] == opcodes_table_g[jump_gc].code) {
+            if (asm_trace_level >= 4) printf("...Deleting branch\n");
+            zcode_markers[i-2] = DELETED_MV;
+            zcode_markers[i-1] = DELETED_MV;
+            zcode_markers[i] = DELETED_MV;
+            zcode_markers[i+1] = DELETED_MV;
+            zcode_markers[i+2] = DELETED_MV;
+            zcode_markers[i+3] = DELETED_MV;
+        }
+        else if (addr >= -0x80 && addr < 0x80) {
             if (asm_trace_level >= 4) printf("...Byte form\n");
             zcode_markers[i+1] = DELETED_MV;
             zcode_markers[i+2] = DELETED_MV;
             zcode_markers[i+3] = DELETED_MV;
-            opmodebyte = i - ((opmodeoffset+1)/2);
             if ((opmodeoffset & 1) == 0)
                 zcode_holding_area[opmodebyte] = 
                     (zcode_holding_area[opmodebyte] & 0xF0) | 0x01;
@@ -2005,7 +2288,6 @@ static void transfer_routine_g(void)
             if (asm_trace_level >= 4) printf("...Short form\n");
             zcode_markers[i+2] = DELETED_MV;
             zcode_markers[i+3] = DELETED_MV;
-            opmodebyte = i - ((opmodeoffset+1)/2);
             if ((opmodeoffset & 1) == 0)
                 zcode_holding_area[opmodebyte] = 
                     (zcode_holding_area[opmodebyte] & 0xF0) | 0x02;
@@ -2028,18 +2310,19 @@ static void transfer_routine_g(void)
         printf("Opening label: %d\n", first_label);
         for (i=0;i<next_label;i++)
             printf("Label %d offset %04x next -> %d previous -> %d\n",
-                i, label_offsets[i], label_next[i], label_prev[i]);
+                i, labels[i].offset, labels[i].next, labels[i].prev);
       }
 
+      /* label will advance through the linked list as pc increases. */
       for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
         i<zcode_ha_size; 
         i++, pc++) {
-        while ((label != -1) && (label_offsets[label] == pc)) {
+        while ((label != -1) && (labels[label].offset == pc)) {
             if (asm_trace_level >= 4)
                 printf("Position of L%d corrected from %04x to %04x\n",
-                label, label_offsets[label], new_pc);
-            label_offsets[label] = new_pc;
-            label = label_next[label];
+                label, labels[label].offset, new_pc);
+            labels[label].offset = new_pc;
+            label = labels[label].next;
         }
         if (zcode_markers[i] != DELETED_MV) new_pc++;
       }
@@ -2049,6 +2332,8 @@ static void transfer_routine_g(void)
             operands with offsets to those labels.  Also issue markers, now
             that we know where they occur in the final Z-code area.          */
 
+    ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
+    
     for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++) {
 
       if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
@@ -2070,7 +2355,13 @@ static void transfer_routine_g(void)
            after it. */
         offset_of_next = new_pc + form_len;
 
-        addr = (label_offsets[j] - offset_of_next) + 2;
+        if (labels[j].offset < 0) {
+            error("Attempt to jump to an unreachable label");
+            addr = 0;
+        }
+        else {
+            addr = (labels[j].offset - offset_of_next) + 2;
+        }
         if (asm_trace_level >= 4) {
             printf("Branch at offset %04x: %04x (%s)\n",
                 new_pc, addr, ((form_len == 1) ? "byte" :
@@ -2095,7 +2386,7 @@ static void transfer_routine_g(void)
             zcode_holding_area[i+2] = (addr >> 8) & 0xFF;
             zcode_holding_area[i+3] = (addr) & 0xFF;
         }
-        transfer_byte(zcode_holding_area + i); new_pc++;
+        zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
       }
       else if (zcode_markers[i] == LABEL_MV) {
           error("*** No LABEL opcodes in Glulx ***");
@@ -2124,26 +2415,27 @@ static void transfer_routine_g(void)
              Then a byte indicating the data size to be patched (1, 2, 4).
              Then the four-byte address (new_pc).
           */
-          write_byte_to_memory_block(&zcode_backpatch_table,
-            zcode_backpatch_size++,
-            zcode_markers[i]);
-          write_byte_to_memory_block(&zcode_backpatch_table,
-            zcode_backpatch_size++,
-            4);
-          write_byte_to_memory_block(&zcode_backpatch_table,
-            zcode_backpatch_size++, ((new_pc >> 24) & 0xFF));
-          write_byte_to_memory_block(&zcode_backpatch_table,
-            zcode_backpatch_size++, ((new_pc >> 16) & 0xFF));
-          write_byte_to_memory_block(&zcode_backpatch_table,
-            zcode_backpatch_size++, ((new_pc >> 8) & 0xFF));
-          write_byte_to_memory_block(&zcode_backpatch_table,
-            zcode_backpatch_size++, (new_pc & 0xFF));
+          if (bpatch_trace_setting >= 2)
+              printf("BP added: MV %d size %d PC %04x\n", zcode_markers[i], 4, new_pc);
+          ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+6);
+          zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i];
+          zcode_backpatch_table[zcode_backpatch_size++] = 4;
+          zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 24) & 0xFF);
+          zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 16) & 0xFF);
+          zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 8) & 0xFF);
+          zcode_backpatch_table[zcode_backpatch_size++] = (new_pc & 0xFF);
           break;
         }
-        transfer_byte(zcode_holding_area + i); new_pc++;
+        zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
       }
     }
 
+    /* Consistency check */
+    if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
+    {
+        fatalerror("Optimisation increased routine length or failed to match; should not happen");
+    }
+
     if (asm_trace_level >= 3)
     {   printf("After branch optimisation, routine length is %d bytes\n",
              new_pc - rstart_pc);
@@ -2213,7 +2505,22 @@ void assemblez_1_to(int internal_number,
 
 void assemblez_1_branch(int internal_number,
     assembly_operand o1, int label, int flag)
-{   AI.internal_number = internal_number;
+{
+    /* Some clever optimizations first. A constant is always or never equal
+       to zero. */
+    if (o1.marker == 0 && is_constant_ot(o1.type)) {
+        if (internal_number == jz_zc) {
+            if ((flag && o1.value == 0) || (!flag && o1.value != 0)) {
+                assemblez_jump(label);
+                return;
+            }
+            else {
+                /* assemble nothing at all! */
+                return;
+            }
+        }
+    }
+    AI.internal_number = internal_number;
     AI.operand_count = 1;
     AI.operand[0] = o1;
     AI.branch_label_number = label;
@@ -2563,9 +2870,6 @@ void assembleg_1_branch(int internal_number,
         if ((internal_number == jz_gc && o1.value == 0)
           || (internal_number == jnz_gc && o1.value != 0)) {
             assembleg_0_branch(jump_gc, label);
-            /* We clear the "can't reach statement" flag here, 
-               so that "if (1)" doesn't produce that warning. */
-            execution_never_reaches_here = 0;
             return;
         }
         if ((internal_number == jz_gc && o1.value != 0)
@@ -2778,9 +3082,11 @@ T (text), I (indirect addressing), F** (set this Flags 2 bit)");
         get_next_token();
         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
         {   assemblez_instruction(&AI);
+            AI.text = NULL;
             return;
         }
         ebf_error("semicolon ';' after print string", token_text);
+        AI.text = NULL;
         put_token_back();
         return;
     }
@@ -2803,11 +3109,11 @@ T (text), I (indirect addressing), F** (set this Flags 2 bit)");
             else
             {   if (strcmp(token_text, "sp") == 0) n = 0;
                 else
-                {   if (stypes[token_value] != GLOBAL_VARIABLE_T)
+                {   if (symbols[token_value].type != GLOBAL_VARIABLE_T)
                         error_named(
                             "Store '->' destination not 'sp' or a variable:",
                             token_text);
-                    else n = svals[token_value];
+                    else n = symbols[token_value].value;
                 }
             }
             AI.store_variable_number = n;
@@ -2961,6 +3267,7 @@ static void parse_assembly_g(void)
   int error_flag = FALSE, is_macro = FALSE;
 
   AI.operand_count = 0;
+  AI.text = NULL;
 
   opcode_names.enabled = TRUE;
   opcode_macros.enabled = TRUE;
@@ -3114,15 +3421,6 @@ extern void parse_assembly(void)
 /*   Data structure management routines                                      */
 /* ------------------------------------------------------------------------- */
 
-extern void asm_begin_pass(void)
-{   no_instructions = 0;
-    zmachine_pc = 0;
-    no_sequence_points = 0;
-    next_label = 0;
-    next_sequence_point = 0;
-    zcode_ha_size = 0;
-}
-
 extern void init_asm_vars(void)
 {   int i;
 
@@ -3132,56 +3430,76 @@ extern void init_asm_vars(void)
     uses_memheap_features = FALSE;
     uses_acceleration_features = FALSE;
     uses_float_features = FALSE;
+    uses_extundo_features = FALSE;
 
+    labels = NULL;
+    sequence_points = NULL;
     sequence_point_follows = TRUE;
     label_moved_error_already_given = FALSE;
 
-    initialise_memory_block(&zcode_area);
+    zcode_area = NULL;
 }
 
-extern void asm_allocate_arrays(void)
-{   if ((debugfile_switch) && (MAX_LABELS < 2000)) MAX_LABELS = 2000;
-
-    variable_tokens = my_calloc(sizeof(int32),  
-        MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable tokens");
-    variable_usage = my_calloc(sizeof(int),  
-        MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable usage");
-
-    label_offsets = my_calloc(sizeof(int32), MAX_LABELS, "label offsets");
-    label_symbols = my_calloc(sizeof(int32), MAX_LABELS, "label symbols");
-    label_next = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
-    label_prev = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
-    sequence_point_labels
-        = my_calloc(sizeof(int), MAX_LABELS, "sequence point labels");
-    sequence_point_locations
-        = my_calloc(sizeof(debug_location),
-                    MAX_LABELS,
-                    "sequence point locations");
-
-    zcode_holding_area = my_malloc(MAX_ZCODE_SIZE,"compiled routine code area");
-    zcode_markers = my_malloc(MAX_ZCODE_SIZE, "compiled routine code area");
+extern void asm_begin_pass(void)
+{   no_instructions = 0;
+    zmachine_pc = 0;
+    no_sequence_points = 0;
+    next_label = 0;
+    labeluse_size = 0;
+    next_sequence_point = 0;
+    zcode_ha_size = 0;
+    execution_never_reaches_here = EXECSTATE_REACHABLE;
+}
 
-    named_routine_symbols
-        = my_calloc(sizeof(int32), MAX_SYMBOLS, "named routine symbols");
+extern void asm_allocate_arrays(void)
+{
+    initialise_memory_list(&variables_memlist,
+        sizeof(variableinfo), 200, (void**)&variables,
+        "variables");
+
+    initialise_memory_list(&labels_memlist,
+        sizeof(labelinfo), 1000, (void**)&labels,
+        "labels");
+    initialise_memory_list(&labeluse_memlist,
+        sizeof(int), 1000, (void**)&labeluse,
+        "labeluse");
+    initialise_memory_list(&sequence_points_memlist,
+        sizeof(sequencepointinfo), 1000, (void**)&sequence_points,
+        "sequence points");
+
+    initialise_memory_list(&zcode_holding_area_memlist,
+        sizeof(uchar), 2000, (void**)&zcode_holding_area,
+        "compiled routine code area");
+    initialise_memory_list(&zcode_markers_memlist,
+        sizeof(uchar), 2000, (void**)&zcode_markers,
+        "compiled routine markers area");
+
+    initialise_memory_list(&named_routine_symbols_memlist,
+        sizeof(int32), 1000, (void**)&named_routine_symbols,
+        "named routine symbols");
+
+    initialise_memory_list(&zcode_area_memlist,
+        sizeof(uchar), 8192, (void**)&zcode_area,
+        "code area");
+
+    initialise_memory_list(&current_routine_name,
+        sizeof(char), 3*MAX_IDENTIFIER_LENGTH, NULL,
+        "routine name currently being defined");
 }
 
 extern void asm_free_arrays(void)
 {
-    my_free(&variable_tokens, "variable tokens");
-    my_free(&variable_usage, "variable usage");
+    deallocate_memory_list(&variables_memlist);
 
-    my_free(&label_offsets, "label offsets");
-    my_free(&label_symbols, "label symbols");
-    my_free(&label_next, "label dll 1");
-    my_free(&label_prev, "label dll 2");
-    my_free(&sequence_point_labels, "sequence point labels");
-    my_free(&sequence_point_locations, "sequence point locations");
+    deallocate_memory_list(&labels_memlist);
+    deallocate_memory_list(&sequence_points_memlist);
 
-    my_free(&zcode_holding_area, "compiled routine code area");
-    my_free(&zcode_markers, "compiled routine code markers");
+    deallocate_memory_list(&zcode_holding_area_memlist);
+    deallocate_memory_list(&zcode_markers_memlist);
 
-    my_free(&named_routine_symbols, "named routine symbols");
-    deallocate_memory_block(&zcode_area);
+    deallocate_memory_list(&named_routine_symbols_memlist);
+    deallocate_memory_list(&zcode_area_memlist);
+    deallocate_memory_list(&current_routine_name);
 }
 
 /* ========================================================================= */
index daca6fbe4552f28b0786ca91b044369d47fbe85c..1fd984b0c9b8ebf8c51a25b8287afca0f9a12a83 100644 (file)
@@ -2,8 +2,8 @@
 /*   "bpatch" : Keeps track of, and finally acts on, backpatch markers,      */
 /*              correcting symbol values not known at compilation time       */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
 
 #include "header.h"
 
-memory_block zcode_backpatch_table, staticarray_backpatch_table,
-    zmachine_backpatch_table;
+uchar *staticarray_backpatch_table; /* Allocated to staticarray_backpatch_size */
+memory_list staticarray_backpatch_table_memlist;
+uchar *zmachine_backpatch_table; /* Allocated to zmachine_backpatch_size */
+memory_list zmachine_backpatch_table_memlist;
+uchar *zcode_backpatch_table; /* Allocated to zcode_backpatch_size */
+memory_list zcode_backpatch_table_memlist;
 int32 zcode_backpatch_size, staticarray_backpatch_size,
     zmachine_backpatch_size;
 
@@ -38,7 +42,7 @@ static int32 backpatch_value_z(int32 value)
 
     ASSERT_ZCODE();
 
-    if (asm_trace_level >= 4)
+    if (bpatch_trace_setting)
         printf("BP %s applied to %04x giving ",
             describe_mv(backpatch_marker), value);
 
@@ -86,7 +90,7 @@ static int32 backpatch_value_z(int32 value)
             value = value_of_system_constant(value); break;
         case DWORD_MV:
             value = dictionary_offset + 7 +
-                    final_dict_order[value]*((version_number==3)?7:9);
+                    final_dict_order[value]*(DICT_ENTRY_BYTE_LENGTH);
             break;
         case ACTION_MV:
             break;
@@ -105,10 +109,10 @@ static int32 backpatch_value_z(int32 value)
             break;
         case MAIN_MV:
             value = symbol_index("Main", -1);
-            if (stypes[value] != ROUTINE_T)
+            if (symbols[value].type != ROUTINE_T)
                 error("No 'Main' routine has been defined");
-            sflags[value] |= USED_SFLAG;
-            value = svals[value];
+            symbols[value].flags |= USED_SFLAG;
+            value = symbols[value].value;
             if (OMIT_UNUSED_ROUTINES)
                 value = df_stripped_address_for_address(value);
             value += code_offset/scale_factor;
@@ -123,34 +127,34 @@ static int32 backpatch_value_z(int32 value)
                 value = 0;
                 break;
             }
-            if (sflags[value] & UNKNOWN_SFLAG)
-            {   if (!(sflags[value] & UERROR_SFLAG))
-                {   sflags[value] |= UERROR_SFLAG;
+            if (symbols[value].flags & UNKNOWN_SFLAG)
+            {   if (!(symbols[value].flags & UERROR_SFLAG))
+                {   symbols[value].flags |= UERROR_SFLAG;
                     error_named_at("No such constant as",
-                        (char *) symbs[value], slines[value]);
+                        symbols[value].name, symbols[value].line);
                 }
             }
             else
-            if (sflags[value] & CHANGE_SFLAG)
-            {   sflags[value] &= (~(CHANGE_SFLAG));
-                backpatch_marker = (svals[value]/0x10000);
+            if (symbols[value].flags & CHANGE_SFLAG)
+            {   symbols[value].flags &= (~(CHANGE_SFLAG));
+                backpatch_marker = (symbols[value].marker);
                 if ((backpatch_marker < 0)
                     || (backpatch_marker > LARGEST_BPATCH_MV))
                 {
                     if (no_link_errors == 0)
                     {   compiler_error_named(
                         "Illegal backpatch marker attached to symbol",
-                        (char *) symbs[value]);
+                        symbols[value].name);
                         backpatch_error_flag = TRUE;
                     }
                 }
                 else
-                    svals[value] = backpatch_value_z((svals[value]) % 0x10000);
+                    symbols[value].value = backpatch_value_z((symbols[value].value) % 0x10000);
             }
 
-            sflags[value] |= USED_SFLAG;
-            {   int t = stypes[value];
-                value = svals[value];
+            symbols[value].flags |= USED_SFLAG;
+            {   int t = symbols[value].type;
+                value = symbols[value].value;
                 switch(t)
                 {   case ROUTINE_T: 
                         if (OMIT_UNUSED_ROUTINES)
@@ -172,7 +176,7 @@ static int32 backpatch_value_z(int32 value)
             break;
     }
 
-    if (asm_trace_level >= 4) printf(" %04x\n", value);
+    if (bpatch_trace_setting) printf(" %04x\n", value);
 
     return(value);
 }
@@ -183,7 +187,7 @@ static int32 backpatch_value_g(int32 value)
 
     ASSERT_GLULX();
 
-    if (asm_trace_level >= 4)
+    if (bpatch_trace_setting)
         printf("BP %s applied to %04x giving ",
             describe_mv(backpatch_marker), value);
 
@@ -255,10 +259,10 @@ static int32 backpatch_value_g(int32 value)
             break;
         case MAIN_MV:
             value = symbol_index("Main", -1);
-            if (stypes[value] != ROUTINE_T)
+            if (symbols[value].type != ROUTINE_T)
                 error("No 'Main' routine has been defined");
-            sflags[value] |= USED_SFLAG;
-            value = svals[value];
+            symbols[value].flags |= USED_SFLAG;
+            value = symbols[value].value;
             if (OMIT_UNUSED_ROUTINES)
                 value = df_stripped_address_for_address(value);
             value += code_offset;
@@ -273,34 +277,34 @@ static int32 backpatch_value_g(int32 value)
                 value = 0;
                 break;
             }
-            if (sflags[value] & UNKNOWN_SFLAG)
-            {   if (!(sflags[value] & UERROR_SFLAG))
-                {   sflags[value] |= UERROR_SFLAG;
+            if (symbols[value].flags & UNKNOWN_SFLAG)
+            {   if (!(symbols[value].flags & UERROR_SFLAG))
+                {   symbols[value].flags |= UERROR_SFLAG;
                     error_named_at("No such constant as",
-                        (char *) symbs[value], slines[value]);
+                        symbols[value].name, symbols[value].line);
                 }
             }
             else
-            if (sflags[value] & CHANGE_SFLAG)
-            {   sflags[value] &= (~(CHANGE_SFLAG));
-                backpatch_marker = smarks[value];
+            if (symbols[value].flags & CHANGE_SFLAG)
+            {   symbols[value].flags &= (~(CHANGE_SFLAG));
+                backpatch_marker = symbols[value].marker;
                 if ((backpatch_marker < 0)
                     || (backpatch_marker > LARGEST_BPATCH_MV))
                 {
                     if (no_link_errors == 0)
                     {   compiler_error_named(
                         "Illegal backpatch marker attached to symbol",
-                        (char *) symbs[value]);
+                        symbols[value].name);
                         backpatch_error_flag = TRUE;
                     }
                 }
                 else
-                    svals[value] = backpatch_value_g(svals[value]);
+                    symbols[value].value = backpatch_value_g(symbols[value].value);
             }
 
-            sflags[value] |= USED_SFLAG;
-            {   int t = stypes[value];
-                value = svals[value];
+            symbols[value].flags |= USED_SFLAG;
+            {   int t = symbols[value].type;
+                value = symbols[value].value;
                 switch(t)
                 {
                     case ROUTINE_T:
@@ -340,7 +344,7 @@ symbol");
             break;
     }
 
-    if (asm_trace_level >= 4) printf(" %04x\n", value);
+    if (bpatch_trace_setting) printf(" %04x\n", value);
 
     return(value);
 }
@@ -363,16 +367,14 @@ static void backpatch_zmachine_z(int mv, int zmachine_area, int32 offset)
         if (mv == ACTION_MV) return;
     }
 
-    /* printf("MV %d ZA %d Off %04x\n", mv, zmachine_area, offset); */
+    if (bpatch_trace_setting >= 2)
+        printf("BP added: MV %d ZA %d Off %04x\n", mv, zmachine_area, offset);
 
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, mv);
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, zmachine_area);
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, offset/256);
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, offset%256);
+    ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+4);
+    zmachine_backpatch_table[zmachine_backpatch_size++] = mv;
+    zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area;
+    zmachine_backpatch_table[zmachine_backpatch_size++] = offset/256;
+    zmachine_backpatch_table[zmachine_backpatch_size++] = offset%256;
 }
 
 static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset)
@@ -390,20 +392,16 @@ static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset)
    Then the four-byte address.
 */
 
-/*    printf("+MV %d ZA %d Off %06x\n", mv, zmachine_area, offset);  */
-
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, mv);
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, zmachine_area);
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, (offset >> 24) & 0xFF);
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, (offset >> 16) & 0xFF);
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, (offset >> 8) & 0xFF);
-    write_byte_to_memory_block(&zmachine_backpatch_table,
-        zmachine_backpatch_size++, (offset) & 0xFF);
+    if (bpatch_trace_setting >= 2)
+        printf("BP added: MV %d ZA %d Off %06x\n", mv, zmachine_area, offset);
+
+    ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+6);
+    zmachine_backpatch_table[zmachine_backpatch_size++] = mv;
+    zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area;
+    zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 24) & 0xFF;
+    zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 16) & 0xFF;
+    zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 8) & 0xFF;
+    zmachine_backpatch_table[zmachine_backpatch_size++] = (offset) & 0xFF;
 }
 
 extern void backpatch_zmachine(int mv, int zmachine_area, int32 offset)
@@ -420,12 +418,12 @@ extern void backpatch_zmachine_image_z(void)
     backpatch_error_flag = FALSE;
     while (bm < zmachine_backpatch_size)
     {   backpatch_marker
-            = read_byte_from_memory_block(&zmachine_backpatch_table, bm);
+            = zmachine_backpatch_table[bm];
         zmachine_area
-            = read_byte_from_memory_block(&zmachine_backpatch_table, bm+1);
+            = zmachine_backpatch_table[bm+1];
         offset
-          = 256*read_byte_from_memory_block(&zmachine_backpatch_table,bm+2)
-            + read_byte_from_memory_block(&zmachine_backpatch_table, bm+3);
+          = 256*zmachine_backpatch_table[bm+2]
+            + zmachine_backpatch_table[bm+3];
         bm += 4;
 
         switch(zmachine_area)
@@ -462,19 +460,17 @@ extern void backpatch_zmachine_image_g(void)
     backpatch_error_flag = FALSE;
     while (bm < zmachine_backpatch_size)
     {   backpatch_marker
-            = read_byte_from_memory_block(&zmachine_backpatch_table, bm);
+            = zmachine_backpatch_table[bm];
         zmachine_area
-            = read_byte_from_memory_block(&zmachine_backpatch_table, bm+1);
-        offset = read_byte_from_memory_block(&zmachine_backpatch_table, bm+2);
+            = zmachine_backpatch_table[bm+1];
+        offset = zmachine_backpatch_table[bm+2];
         offset = (offset << 8) |
-          read_byte_from_memory_block(&zmachine_backpatch_table, bm+3);
+          zmachine_backpatch_table[bm+3];
         offset = (offset << 8) |
-          read_byte_from_memory_block(&zmachine_backpatch_table, bm+4);
+          zmachine_backpatch_table[bm+4];
         offset = (offset << 8) |
-          read_byte_from_memory_block(&zmachine_backpatch_table, bm+5);
-            bm += 6;
-
-        /* printf("-MV %d ZA %d Off %06x\n", backpatch_marker, zmachine_area, offset);  */
+          zmachine_backpatch_table[bm+5];
+        bm += 6;
 
             switch(zmachine_area) {   
         case PROP_DEFAULTS_ZA:   addr = prop_defaults_offset+4; break;
@@ -514,9 +510,9 @@ extern void backpatch_zmachine_image_g(void)
 /* ------------------------------------------------------------------------- */
 
 extern void init_bpatch_vars(void)
-{   initialise_memory_block(&zcode_backpatch_table);
-    initialise_memory_block(&staticarray_backpatch_table);
-    initialise_memory_block(&zmachine_backpatch_table);
+{   zcode_backpatch_table = NULL;
+    staticarray_backpatch_table = NULL;
+    zmachine_backpatch_table = NULL;
 }
 
 extern void bpatch_begin_pass(void)
@@ -527,12 +523,21 @@ extern void bpatch_begin_pass(void)
 
 extern void bpatch_allocate_arrays(void)
 {
+    initialise_memory_list(&zcode_backpatch_table_memlist,
+        sizeof(uchar), 128, (void**)&zcode_backpatch_table,
+        "code backpatch table");
+    initialise_memory_list(&staticarray_backpatch_table_memlist,
+        sizeof(uchar), 128, (void**)&staticarray_backpatch_table,
+        "static array backpatch table");
+    initialise_memory_list(&zmachine_backpatch_table_memlist,
+        sizeof(uchar), 128, (void**)&zmachine_backpatch_table,
+        "machine backpatch table");
 }
 
 extern void bpatch_free_arrays(void)
-{   deallocate_memory_block(&zcode_backpatch_table);
-    deallocate_memory_block(&staticarray_backpatch_table);
-    deallocate_memory_block(&zmachine_backpatch_table);
+{   deallocate_memory_list(&zcode_backpatch_table_memlist);
+    deallocate_memory_list(&staticarray_backpatch_table_memlist);
+    deallocate_memory_list(&zmachine_backpatch_table_memlist);
 }
 
 /* ========================================================================= */
index 239d576fb26854133fbc5757e81cca881d24063b..52012114933e2c7fe531e682ba9710c033cfff2e 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "chars" : Character set mappings and the Z-machine alphabet table       */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
index 15831728118b840102d401f7f67b7619c996a21e..388ac861bcae9ba46f13fbc033ab4a5b4824f03a 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "directs" : Directives (# commands)                                     */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -108,21 +108,21 @@ extern int parse_given_directive(int internal_flag)
                return FALSE;
 
            if (!glulx_mode && no_abbreviations==96)
-           {   error("All 96 Z-machine abbreviations already declared");
+           {   error_max_abbreviations(no_abbreviations);
+               panic_mode_error_recovery(); return FALSE;
+           }
+           if (!glulx_mode && no_abbreviations==MAX_ABBREVS)
+           {   error_max_abbreviations(no_abbreviations);
+               /* This is no longer a memoryerror(); MAX_ABBREVS is an authoring decision for Z-code games. */
                panic_mode_error_recovery(); return FALSE;
            }
-           if (no_abbreviations==MAX_ABBREVS)
-               memoryerror("MAX_ABBREVS", MAX_ABBREVS);
 
            if (abbrevs_lookup_table_made)
            {   error("All abbreviations must be declared together");
                panic_mode_error_recovery(); return FALSE;
            }
            if (token_type != DQ_TT)
-               return ebf_error_recover("abbreviation string", token_text);
-           if (strlen(token_text)<2)
-           {   error_named("It's not worth abbreviating", token_text);
-               continue;
+           {   return ebf_error_recover("abbreviation string", token_text);
            }
            /* Abbreviation string with null must fit in a MAX_ABBREV_LENGTH
               array. */
@@ -170,9 +170,9 @@ extern int parse_given_directive(int internal_flag)
             return ebf_error_recover("new constant name", token_text);
         }
 
-        if (!(sflags[i] & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG)))
+        if (!(symbols[i].flags & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG)))
         {   discard_token_location(beginning_debug_location);
-            return ebf_symbol_error_recover("new constant name", token_text, typename(stypes[i]), slines[i]);
+            return ebf_symbol_error_recover("new constant name", token_text, typename(symbols[i].type), symbols[i].line);
         }
 
         assign_symbol(i, 0, CONSTANT_T);
@@ -181,7 +181,7 @@ extern int parse_given_directive(int internal_flag)
         get_next_token();
 
         if ((token_type == SEP_TT) && (token_value == COMMA_SEP))
-        {   if (debugfile_switch && !(sflags[i] & REDEFINABLE_SFLAG))
+        {   if (debugfile_switch && !(symbols[i].flags & REDEFINABLE_SFLAG))
             {   debug_file_printf("<constant>");
                 debug_file_printf("<identifier>%s</identifier>", constant_name);
                 write_debug_symbol_optional_backpatch(i);
@@ -192,7 +192,7 @@ extern int parse_given_directive(int internal_flag)
         }
 
         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
-        {   if (debugfile_switch && !(sflags[i] & REDEFINABLE_SFLAG))
+        {   if (debugfile_switch && !(symbols[i].flags & REDEFINABLE_SFLAG))
             {   debug_file_printf("<constant>");
                 debug_file_printf("<identifier>%s</identifier>", constant_name);
                 write_debug_symbol_optional_backpatch(i);
@@ -209,7 +209,7 @@ extern int parse_given_directive(int internal_flag)
             if (AO.marker != 0)
             {   assign_marked_symbol(i, AO.marker, AO.value,
                     CONSTANT_T);
-                sflags[i] |= CHANGE_SFLAG;
+                symbols[i].flags |= CHANGE_SFLAG;
                 if (i == grammar_version_symbol)
                     error(
                 "Grammar__Version must be given an explicit constant value");
@@ -228,7 +228,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
             }
         }
 
-        if (debugfile_switch && !(sflags[i] & REDEFINABLE_SFLAG))
+        if (debugfile_switch && !(symbols[i].flags & REDEFINABLE_SFLAG))
         {   debug_file_printf("<constant>");
             debug_file_printf("<identifier>%s</identifier>", constant_name);
             write_debug_symbol_optional_backpatch(i);
@@ -258,9 +258,9 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
             return ebf_error_recover("name", token_text);
 
         i = -1;
-        if (sflags[token_value] & UNKNOWN_SFLAG)
+        if (symbols[token_value].flags & UNKNOWN_SFLAG)
         {   i = token_value;
-            sflags[i] |= DEFCON_SFLAG;
+            symbols[i].flags |= DEFCON_SFLAG;
         }
 
         get_next_token();
@@ -273,7 +273,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
             {   if (AO.marker != 0)
                 {   assign_marked_symbol(i, AO.marker, AO.value,
                         CONSTANT_T);
-                    sflags[i] |= CHANGE_SFLAG;
+                    symbols[i].flags |= CHANGE_SFLAG;
                 }
                 else assign_symbol(i, AO.value, CONSTANT_T);
             }
@@ -328,6 +328,8 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
                 else {
                     assembly_operand AO;
                     put_token_back();
+                    if (ZCODE_LESS_DICT_DATA && !glulx_mode)
+                        warning("The third dictionary field will be ignored because ZCODE_LESS_DICT_DATA is set");
                     AO = parse_expression(CONSTANT_CONTEXT);
                     if (AO.marker != 0)
                         error("A definite value must be given as a Dictionary flag");
@@ -405,17 +407,28 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         if (token_type != SYMBOL_TT)
             return ebf_error_recover("symbol name", token_text);
 
+        /* Special case: a symbol of the form "VN_nnnn" is considered
+           defined if the compiler version number is at least nnnn.
+           Compiler version numbers look like "1640" for Inform 6.40;
+           see RELEASE_NUMBER.
+           ("VN_nnnn" isn't a real symbol and can't be used in other
+           contexts.) */
         if ((token_text[0] == 'V')
             && (token_text[1] == 'N')
             && (token_text[2] == '_')
             && (strlen(token_text)==7))
-        {   i = atoi(token_text+3);
-            if (VNUMBER < i) flag = (flag)?FALSE:TRUE;
-            goto HashIfCondition;
+        {
+            char *endstr;
+            i = strtol(token_text+3, &endstr, 10);
+            if (*endstr == '\0') {
+                /* All characters after the underscore were digits */
+                if (VNUMBER < i) flag = (flag)?FALSE:TRUE;
+                goto HashIfCondition;
+            }
         }
 
-        if (sflags[token_value] & UNKNOWN_SFLAG) flag = (flag)?FALSE:TRUE;
-        else sflags[token_value] |= USED_SFLAG;
+        if (symbols[token_value].flags & UNKNOWN_SFLAG) flag = (flag)?FALSE:TRUE;
+        else symbols[token_value].flags |= USED_SFLAG;
         goto HashIfCondition;
 
     case IFNOT_CODE:
@@ -428,14 +441,17 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         {   dont_enter_into_symbol_table = -2; n = 1;
             directives.enabled = TRUE;
             do
-            {   get_next_token();
+            {
+                release_token_texts();
+                get_next_token();
                 if (token_type == EOF_TT)
                 {   error("End of file reached in code 'If...'d out");
                     directives.enabled = FALSE;
                     return TRUE;
                 }
                 if (token_type == DIRECTIVE_TT)
-                {   switch(token_value)
+                {
+                    switch(token_value)
                     {   case ENDIF_CODE:
                             n--; break;
                         case IFV3_CODE:
@@ -508,7 +524,9 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         {   dont_enter_into_symbol_table = -2; n = 1;
             directives.enabled = TRUE;
             do
-            {   get_next_token();
+            {
+                release_token_texts();
+                get_next_token();
                 if (token_type == EOF_TT)
                 {   error("End of file reached in code 'If...'d out");
                     directives.enabled = FALSE;
@@ -597,6 +615,10 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         get_next_token();
         if (token_type != DQ_TT)
             return ebf_error_recover("filename in double-quotes", token_text);
+        if (strlen(token_text) >= PATHLEN-1) {
+            error_numbered("'Link' filename is too long; max length is", PATHLEN-1);
+            break;
+        }
         link_module(token_text);                           /* See "linker.c" */
         break;
 
@@ -620,8 +642,8 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         get_next_token(); i = token_value;
         if (token_type != SYMBOL_TT)
             return ebf_error_recover("new low string name", token_text);
-        if (!(sflags[i] & UNKNOWN_SFLAG))
-            return ebf_symbol_error_recover("new low string name", token_text, typename(stypes[i]), slines[i]);
+        if (!(symbols[i].flags & UNKNOWN_SFLAG))
+            return ebf_symbol_error_recover("new low string name", token_text, typename(symbols[i].type), symbols[i].line);
 
         get_next_token();
         if (token_type != DQ_TT)
@@ -761,7 +783,10 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         break;
 
     /* --------------------------------------------------------------------- */
-    /*   Property [long] [additive] name [alias oldname]                     */
+    /*   Property [long] [additive] name                                     */
+    /*   Property [long] [additive] name alias oldname                       */
+    /*   Property [long] [additive] name defaultvalue                        */
+    /*   Property [long] individual name                                     */
     /* --------------------------------------------------------------------- */
 
     case PROPERTY_CODE: make_property(); break;           /* See "objects.c" */
@@ -812,10 +837,10 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
 
         if (token_type != SYMBOL_TT)
             return ebf_error_recover("name of routine to replace", token_text);
-        if (!(sflags[token_value] & UNKNOWN_SFLAG))
+        if (!(symbols[token_value].flags & UNKNOWN_SFLAG))
             return ebf_error_recover("name of routine not yet defined", token_text);
 
-        sflags[token_value] |= REPLACE_SFLAG;
+        symbols[token_value].flags |= REPLACE_SFLAG;
 
         /* If a second symbol is provided, it will refer to the
            original (replaced) definition of the routine. */
@@ -829,7 +854,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         {   return FALSE;
         }
 
-        if (token_type != SYMBOL_TT || !(sflags[token_value] & UNKNOWN_SFLAG))
+        if (token_type != SYMBOL_TT || !(symbols[token_value].flags & UNKNOWN_SFLAG))
             return ebf_error_recover("semicolon ';' or new routine name", token_text);
 
         /* Define the original-form symbol as a zero constant. Its
@@ -891,8 +916,8 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
 
         i = token_value; flag = FALSE;
 
-        if (sflags[i] & UNKNOWN_SFLAG)
-        {   sflags[i] |= STUB_SFLAG;
+        if (symbols[i].flags & UNKNOWN_SFLAG)
+        {   symbols[i].flags |= STUB_SFLAG;
             flag = TRUE;
         }
 
@@ -908,15 +933,17 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         {
             /*  Give these parameter-receiving local variables names
                 for the benefit of the debugging information file,
-                and for assembly tracing to look sensible.                   */
+                and for assembly tracing to look sensible.
+                (We don't set local_variable.keywords because we're not
+                going to be parsing any code.)                               */
 
-            local_variable_texts[0] = "dummy1";
-            local_variable_texts[1] = "dummy2";
-            local_variable_texts[2] = "dummy3";
-            local_variable_texts[3] = "dummy4";
+            strcpy(local_variable_names[0].text, "dummy1");
+            strcpy(local_variable_names[1].text, "dummy2");
+            strcpy(local_variable_names[2].text, "dummy3");
+            strcpy(local_variable_names[3].text, "dummy4");
 
             assign_symbol(i,
-                assemble_routine_header(k, FALSE, (char *) symbs[i], FALSE, i),
+                assemble_routine_header(k, FALSE, symbols[i].name, FALSE, i),
                 ROUTINE_T);
 
             /*  Ensure the return value of a stubbed routine is false,
@@ -929,7 +956,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
 
             /*  Inhibit "local variable unused" warnings  */
 
-            for (i=1; i<=k; i++) variable_usage[i] = 1;
+            for (i=1; i<=k; i++) variables[i].usage = 1;
             sequence_point_follows = FALSE;
             assemble_routine_end(FALSE, get_token_locations());
         }
@@ -966,14 +993,19 @@ the first constant definition");
         declare_systemfile(); break;                        /* see "files.c" */
 
     /* --------------------------------------------------------------------- */
-    /*   Trace dictionary                                                    */
-    /*         objects                                                       */
-    /*         symbols                                                       */
-    /*         verbs                                                         */
-    /*                      [on/off]                                         */
-    /*         assembly     [on/off]                                         */
-    /*         expressions  [on/off]                                         */
-    /*         lines        [on/off]                                         */
+    /*   Trace dictionary   [on/NUM]                                         */
+    /*         objects      [on/NUM]                                         */
+    /*         symbols      [on/NUM]                                         */
+    /*         verbs        [on/NUM]                                         */
+    /*                      [on/off/NUM]      {same as "assembly"}           */
+    /*         assembly     [on/off/NUM]                                     */
+    /*         expressions  [on/off/NUM]                                     */
+    /*         lines        [on/off/NUM]                                     */
+    /*         tokens       [on/off/NUM]                                     */
+    /*         linker       [on/off/NUM]                                     */
+    /*                                                                       */
+    /* The first four trace commands immediately display a compiler table.   */
+    /* The rest set or clear an ongoing trace.                               */
     /* --------------------------------------------------------------------- */
 
     case TRACE_CODE:
@@ -982,62 +1014,105 @@ the first constant definition");
         get_next_token();
         trace_keywords.enabled = FALSE;
         directives.enabled = TRUE;
-        if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
-        {   asm_trace_level = 1; return FALSE; }
+        
+        if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) {
+            /* "Trace;" */
+            put_token_back();
+            i = ASSEMBLY_TK;
+            trace_level = &asm_trace_level;
+            j = 1;
+            goto HandleTraceKeyword;
+        }
+        if (token_type == NUMBER_TT) {
+            /* "Trace NUM;" */
+            i = ASSEMBLY_TK;
+            trace_level = &asm_trace_level;
+            j = token_value;
+            goto HandleTraceKeyword;
+        }
 
+        /* Anything else must be "Trace KEYWORD..." Remember that
+           'on' and 'off' are trace keywords. */
+        
         if (token_type != TRACE_KEYWORD_TT)
             return ebf_error_recover("debugging keyword", token_text);
 
         trace_keywords.enabled = TRUE;
 
-        i = token_value; j = 0;
+        /* Note that "Trace verbs" doesn't affect list_verbs_setting.
+           It shows the grammar at this point in the code. Setting
+           list_verbs_setting shows the grammar at the end of 
+           compilation.
+           Same goes for "Trace dictionary" and list_dict_setting, etc. */
+        
+        i = token_value;
+
         switch(i)
-        {   case DICTIONARY_TK: break;
-            case OBJECTS_TK:    break;
-            case VERBS_TK:      break;
-            default:
-                switch(token_value)
-                {   case ASSEMBLY_TK:
-                        trace_level = &asm_trace_level;  break;
-                    case EXPRESSIONS_TK:
-                        trace_level = &expr_trace_level; break;
-                    case LINES_TK:
-                        trace_level = &line_trace_level; break;
-                    case TOKENS_TK:
-                        trace_level = &tokens_trace_level; break;
-                    case LINKER_TK:
-                        trace_level = &linker_trace_level; break;
-                    case SYMBOLS_TK:
-                        trace_level = NULL; break;
-                    default:
-                        put_token_back();
-                        trace_level = &asm_trace_level; break;
-                }
-                j = 1;
-                get_next_token();
-                if ((token_type == SEP_TT) &&
-                    (token_value == SEMICOLON_SEP))
-                {   put_token_back(); break;
-                }
-                if (token_type == NUMBER_TT)
-                {   j = token_value; break; }
-                if ((token_type == TRACE_KEYWORD_TT) && (token_value == ON_TK))
-                {   j = 1; break; }
-                if ((token_type == TRACE_KEYWORD_TT) && (token_value == OFF_TK))
-                {   j = 0; break; }
-                put_token_back(); break;
+        {
+        case ASSEMBLY_TK:
+            trace_level = &asm_trace_level;  break;
+        case EXPRESSIONS_TK:
+            trace_level = &expr_trace_level; break;
+        case TOKENS_TK:
+            trace_level = &tokens_trace_level; break;
+        case LINKER_TK:
+            trace_level = &linker_trace_level; break;
+        case DICTIONARY_TK:
+        case SYMBOLS_TK:
+        case OBJECTS_TK:
+        case VERBS_TK:
+            trace_level = NULL; break;
+        case LINES_TK:
+            /* never implememented */
+            trace_level = NULL; break;
+        default:
+            put_token_back();
+            trace_level = &asm_trace_level; break;
         }
+        
+        j = 1;
+        get_next_token();
+        if ((token_type == SEP_TT) &&
+            (token_value == SEMICOLON_SEP))
+        {   put_token_back();
+        }
+        else if (token_type == NUMBER_TT)
+        {   j = token_value;
+        }
+        else if ((token_type == TRACE_KEYWORD_TT) && (token_value == ON_TK))
+        {   j = 1;
+        }
+        else if ((token_type == TRACE_KEYWORD_TT) && (token_value == OFF_TK))
+        {   j = 0;
+        }
+        else
+        {   put_token_back();
+        }
+
+        trace_keywords.enabled = FALSE;
+
+        HandleTraceKeyword:
 
+        if (i == LINES_TK) {
+            warning_named("Trace option is not supported:", trace_keywords.keywords[i]);
+            break;
+        }
+        
+        if (trace_level == NULL && j == 0) {
+            warning_named("Trace directive to display table at 'off' level has no effect: table", trace_keywords.keywords[i]);
+            break;
+        }
+        
         switch(i)
-        {   case DICTIONARY_TK: show_dictionary();  break;
-            case OBJECTS_TK:    list_object_tree(); break;
+        {   case DICTIONARY_TK: show_dictionary(j);  break;
+            case OBJECTS_TK:    list_object_tree();  break;
             case SYMBOLS_TK:    list_symbols(j);     break;
-            case VERBS_TK:      list_verb_table();  break;
+            case VERBS_TK:      list_verb_table();   break;
             default:
-                *trace_level = j;
+                if (trace_level)
+                    *trace_level = j;
                 break;
         }
-        trace_keywords.enabled = FALSE;
         break;
 
     /* --------------------------------------------------------------------- */
@@ -1049,12 +1124,12 @@ the first constant definition");
         if (token_type != SYMBOL_TT)
             return ebf_error_recover("symbol name", token_text);
 
-        if (sflags[token_value] & UNKNOWN_SFLAG)
+        if (symbols[token_value].flags & UNKNOWN_SFLAG)
         {   break; /* undef'ing an undefined constant is okay */
         }
 
-        if (stypes[token_value] != CONSTANT_T)
-        {   error_named("Cannot Undef a symbol which is not a defined constant:", (char *)symbs[token_value]);
+        if (symbols[token_value].type != CONSTANT_T)
+        {   error_named("Cannot Undef a symbol which is not a defined constant:", symbols[token_value].name);
             break;
         }
 
@@ -1062,7 +1137,7 @@ the first constant definition");
         {   write_debug_undef(token_value);
         }
         end_symbol_scope(token_value);
-        sflags[token_value] |= USED_SFLAG;
+        symbols[token_value].flags |= USED_SFLAG;
         break;
 
     /* --------------------------------------------------------------------- */
@@ -1088,21 +1163,46 @@ the first constant definition");
             }
 
             if (AO.marker != 0)
-                error("A definite value must be given as version number");
-            else 
-            if (glulx_mode) 
+            {
+              error("A definite value must be given as version number.");
+              break;
+            }
+            else if (no_routines > 1)
+            {
+              /* The built-in Main__ routine is number zero. */
+              error("A 'Version' directive must come before the first routine definition.");
+              break;
+            }
+            else if (glulx_mode) 
             {
               warning("The Version directive does not work in Glulx. Use \
 -vX.Y.Z instead, as either a command-line argument or a header comment.");
               break;
             }
             else
-            {   i = AO.value;
+            {
+                int debtok;
+                i = AO.value;
                 if ((i<3) || (i>8))
                 {   error("The version number must be in the range 3 to 8");
                     break;
                 }
                 select_version(i);
+                /* We must now do a small dance to reset the DICT_ENTRY_BYTES
+                   constant, which was defined at startup based on the Z-code
+                   version.
+                   The calculation here is repeated from select_target(). */
+                DICT_ENTRY_BYTE_LENGTH = ((version_number==3)?7:9) - (ZCODE_LESS_DICT_DATA?1:0);
+                debtok = symbol_index("DICT_ENTRY_BYTES", -1);
+                if (!(symbols[debtok].flags & UNKNOWN_SFLAG))
+                {
+                    if (!(symbols[debtok].flags & REDEFINABLE_SFLAG))
+                    {
+                        warning("The DICT_ENTRY_BYTES symbol is not marked redefinable");
+                    }
+                    /* Redefine the symbol... */
+                    assign_symbol(debtok, DICT_ENTRY_BYTE_LENGTH, CONSTANT_T);
+                }
             }
         }
         break;                                             /* see "inform.c" */
index 195bfe9f90948bee4ef85c2cec4df79979630d11..09efcd28fcb56f85ff5b101f138338adc82da31f 100644 (file)
@@ -2,8 +2,8 @@
 /*   "errors" : Warnings, errors and fatal errors                            */
 /*              (with error throwback code for RISC OS machines)             */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -197,11 +197,8 @@ extern void fatalerror(char *s)
 #endif
 #ifdef MAC_FACE
     close_all_source();
-    if (temporary_files_switch) remove_temp_files();
     abort_transcript_file();
     free_arrays();
-    if (store_the_text)
-        my_free(&all_text,"transcription text");
     longjmp(g_fallback, 1);
 #endif
     exit(1);
@@ -225,16 +222,6 @@ extern void memory_out_error(int32 size, int32 howmany, char *name)
     fatalerror(error_message_buff);
 }
 
-extern void memoryerror(char *s, int32 size)
-{
-    snprintf(error_message_buff, ERROR_BUFLEN,
-        "The memory setting %s (which is %ld at present) has been \
-exceeded.  Try running Inform again with $%s=<some-larger-number> on the \
-command line.",s,(long int) size,s);
-    ellipsize_error_message_buff();
-    fatalerror(error_message_buff);
-}
-
 /* ------------------------------------------------------------------------- */
 /*   Survivable diagnostics:                                                 */
 /*      compilation errors   style 1                                         */
@@ -244,8 +231,6 @@ command line.",s,(long int) size,s);
 /*                                    indicate a bug in Inform)              */
 /* ------------------------------------------------------------------------- */
 
-static int errors[MAX_ERRORS];
-
 int no_errors, no_warnings, no_suppressed_warnings, no_link_errors,
     no_compiler_errors;
 
@@ -253,7 +238,7 @@ char *forerrors_buff;
 int  forerrors_pointer;
 
 static void message(int style, char *s)
-{   int throw_style = style;
+{
     if (hash_printed_since_newline) printf("\n");
     hash_printed_since_newline = FALSE;
     print_preamble();
@@ -261,22 +246,19 @@ static void message(int style, char *s)
     {   case 1: printf("Error: "); no_errors++; break;
         case 2: printf("Warning: "); no_warnings++; break;
         case 3: printf("Error:  [linking '%s']  ", current_module_filename);
-                no_link_errors++; no_errors++; throw_style=1; break;
+                no_link_errors++; no_errors++; break;
         case 4: printf("*** Compiler error: ");
-                no_compiler_errors++; throw_style=1; break;
+                no_compiler_errors++; break;
     }
     printf(" %s\n", s);
 #ifdef ARC_THROWBACK
-    throwback(throw_style, s);
+    throwback(((style <= 2) ? style : 1), s);
 #endif
 #ifdef MAC_FACE
     ProcessEvents (&g_proc);
     if (g_proc != true)
     {   free_arrays();
-        if (store_the_text)
-            my_free(&all_text,"transcription text");
         close_all_source ();
-        if (temporary_files_switch) remove_temp_files();
         abort_transcript_file();
         longjmp (g_fallback, 1);
     }
@@ -295,7 +277,6 @@ static void message(int style, char *s)
 extern void error(char *s)
 {   if (no_errors == MAX_ERRORS)
         fatalerror("Too many errors: giving up");
-    errors[no_errors] = no_syntax_lines;
     message(1,s);
 }
 
@@ -395,6 +376,30 @@ extern void unicode_char_error(char *s, int32 uni)
     error(error_message_buff);
 }
 
+extern void error_max_dynamic_strings(int index)
+{
+    if (index >= 96 && !glulx_mode)
+        snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(95) may be used in Z-code");
+    else if (MAX_DYNAMIC_STRINGS == 0)
+        snprintf(error_message_buff, ERROR_BUFLEN, "Dynamic strings may not be used, because $MAX_DYNAMIC_STRINGS has been set to 0. Increase MAX_DYNAMIC_STRINGS.");
+    else if (MAX_DYNAMIC_STRINGS == 32 && !glulx_mode)
+        snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(%02d) may be used, because $MAX_DYNAMIC_STRINGS has its default value of %d. Increase MAX_DYNAMIC_STRINGS.", MAX_DYNAMIC_STRINGS-1, MAX_DYNAMIC_STRINGS);
+    else
+        snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(%02d) may be used, because $MAX_DYNAMIC_STRINGS has been set to %d. Increase MAX_DYNAMIC_STRINGS.", MAX_DYNAMIC_STRINGS-1, MAX_DYNAMIC_STRINGS);
+
+    ellipsize_error_message_buff();
+    error(error_message_buff);
+}
+
+extern void error_max_abbreviations(int index)
+{
+    /* This is only called for Z-code. */
+    if (index >= 96)
+        error("The number of abbreviations has exceeded 96, the limit in Z-code");
+    else
+        error("The number of abbreviations has exceeded MAX_ABBREVS. Increase MAX_ABBREVS.");
+}
+
 /* ------------------------------------------------------------------------- */
 /*   Style 2: Warning message routines                                       */
 /* ------------------------------------------------------------------------- */
@@ -419,6 +424,17 @@ extern void warning_named(char *s1, char *s2)
     message(2,error_message_buff);
 }
 
+extern void symtype_warning(char *context, char *name, char *type, char *wanttype)
+{
+    if (nowarnings_switch) { no_suppressed_warnings++; return; }
+    if (name)
+        snprintf(error_message_buff, ERROR_BUFLEN, "In %s, expected %s but found %s \"%s\"", context, wanttype, type, name);
+    else
+        snprintf(error_message_buff, ERROR_BUFLEN, "In %s, expected %s but found %s", context, wanttype, type);
+    ellipsize_error_message_buff();
+    message(2,error_message_buff);
+}
+
 extern void dbnu_warning(char *type, char *name, brief_location report_line)
 {   int i;
     ErrorPosition E = ErrorReport;
@@ -467,7 +483,6 @@ extern void obsolete_warning(char *s1)
 
 extern void link_error(char *s)
 {   if (no_errors==MAX_ERRORS) fatalerror("Too many errors: giving up");
-    errors[no_errors] = no_syntax_lines;
     message(3,s);
 }
 
@@ -484,11 +499,11 @@ extern void link_error_named(char *s1, char *s2)
 extern void print_sorry_message(void)
 {   printf(
 "***********************************************************************\n\
-Compiler errors should never occur if Inform is working properly.\n\
-Check to see if there is a more recent version available, from which\n\
-the problem may have been removed. If not, please report this fault\n\
-and if at all possible, please include your source code, as faults\n\
-such as these are rare and often difficult to reproduce. Sorry.\n\
+* 'Compiler errors' should never occur if Inform is working properly. *\n\
+Check to see if there is a more recent version available, from which\n\
+the problem may have been removed. If not, please report this fault\n\
+and if at all possible, please include your source code, as faults\n\
+such as these are rare and often difficult to reproduce. Sorry.\n\
 ***********************************************************************\n");
 }
 
@@ -580,7 +595,7 @@ extern void errors_begin_pass(void)
 }
 
 extern void errors_allocate_arrays(void)
-{   forerrors_buff = my_malloc(512, "errors buffer");
+{   forerrors_buff = my_malloc(FORERRORS_SIZE, "errors buffer");
 }
 
 extern void errors_free_arrays(void)
index 6651547081a0fb45ffa93c83ac9ff00113ded6b0..f8c75530f67eb365f497e48a8980407c1c9e8b8c 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "expressc" :  The expression code generator                             */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -403,7 +403,7 @@ static void value_in_void_context_z(assembly_operand AO)
         case SHORT_CONSTANT_OT:
             t = "<constant>";
             if (AO.marker == SYMBOL_MV)
-                t = (char *) (symbs[AO.value]);
+                t = (symbols[AO.value].name);
             break;
         case VARIABLE_OT:
             t = variable_name(AO.value);
@@ -466,13 +466,13 @@ static void access_memory_z(int oc, assembly_operand AO1, assembly_operand AO2,
 
         size_ao = zero_ao; size_ao.value = -1;
         for (x=0; x<no_arrays; x++)
-        {   if (((AO1.marker == ARRAY_MV) == (!array_locs[x]))
-                && (AO1.value == svals[array_symbols[x]]))
-            {   size_ao.value = array_sizes[x]; y=x;
+        {   if (((AO1.marker == ARRAY_MV) == (!arrays[x].loc))
+                && (AO1.value == symbols[arrays[x].symbol].value))
+            {   size_ao.value = arrays[x].size; y=x;
             }
         }
         
-        if (array_locs[y] && !read_flag) {
+        if (arrays[y].loc && !read_flag) {
             error("Cannot write to a static array");
         }
 
@@ -480,19 +480,19 @@ static void access_memory_z(int oc, assembly_operand AO1, assembly_operand AO2,
             from_module=TRUE;
         else {
             from_module=FALSE;
-            type_ao = zero_ao; type_ao.value = array_types[y];
+            type_ao = zero_ao; type_ao.value = arrays[y].type;
 
             if ((!is_systemfile()))
             {   if (byte_flag)
                 {
-                    if ((array_types[y] == WORD_ARRAY)
-                        || (array_types[y] == TABLE_ARRAY))
+                    if ((arrays[y].type == WORD_ARRAY)
+                        || (arrays[y].type == TABLE_ARRAY))
                         warning("Using '->' to access a --> or table array");
                 }
                 else
                 {
-                    if ((array_types[y] == BYTE_ARRAY)
-                        || (array_types[y] == STRING_ARRAY))
+                    if ((arrays[y].type == BYTE_ARRAY)
+                        || (arrays[y].type == STRING_ARRAY))
                     warning("Using '-->' to access a -> or string array");
                 }
             }
@@ -520,15 +520,15 @@ static void access_memory_z(int oc, assembly_operand AO1, assembly_operand AO2,
         max_ao = size_ao;
 
         if (byte_flag
-            && ((array_types[y] == WORD_ARRAY)
-                || (array_types[y] == TABLE_ARRAY)))
+            && ((arrays[y].type == WORD_ARRAY)
+                || (arrays[y].type == TABLE_ARRAY)))
         {   max_ao.value = size_ao.value*2 + 1;
             type_ao.value += 8;
         }
         if ((!byte_flag)
-            && ((array_types[y] == BYTE_ARRAY)
-                || (array_types[y] == STRING_ARRAY) 
-                || (array_types[y] == BUFFER_ARRAY)))
+            && ((arrays[y].type == BYTE_ARRAY)
+                || (arrays[y].type == STRING_ARRAY) 
+                || (arrays[y].type == BUFFER_ARRAY)))
         {   if ((size_ao.value % 2) == 0)
                  max_ao.value = size_ao.value/2 - 1;
             else max_ao.value = (size_ao.value-1)/2;
@@ -540,10 +540,10 @@ static void access_memory_z(int oc, assembly_operand AO1, assembly_operand AO2,
         if (max_ao.value >= 256) max_ao.type = LONG_CONSTANT_OT;
 
         /* Can't write to the size entry in a string or table */
-        if (((array_types[y] == STRING_ARRAY)
-             || (array_types[y] == TABLE_ARRAY))
+        if (((arrays[y].type == STRING_ARRAY)
+             || (arrays[y].type == TABLE_ARRAY))
             && (!read_flag))
-        {   if ((array_types[y] == TABLE_ARRAY) && byte_flag)
+        {   if ((arrays[y].type == TABLE_ARRAY) && byte_flag)
                 zero_ao.value = 2;
             else zero_ao.value = 1;
         }
@@ -716,6 +716,25 @@ static void compile_conditional_z(int oc,
 
     ASSERT_ZCODE(); 
 
+    switch (oc) {
+    case test_attr_zc:
+        check_warn_symbol_type(&AO1, OBJECT_T, 0, "\"has/hasnt\" expression");
+        check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"has/hasnt\" expression");
+        break;
+    case jin_zc:
+        check_warn_symbol_type(&AO1, OBJECT_T, 0, "\"in/notin\" expression");
+        check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"in/notin\" expression");
+        break;
+    case 200:
+        /* first argument can be anything */
+        check_warn_symbol_type(&AO2, CLASS_T, 0, "\"ofclass\" expression");
+        break;
+    case 201:
+        /* first argument can be anything */
+        check_warn_symbol_type(&AO2, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"provides\" expression");
+        break;
+    }
+    
     if (oc<200)
     {   if ((runtime_error_checking_switch) && (oc == jin_zc))
         {   if (flag) error_label = next_label++;
@@ -785,7 +804,7 @@ static void value_in_void_context_g(assembly_operand AO)
         case ZEROCONSTANT_OT:
             t = "<constant>";
             if (AO.marker == SYMBOL_MV)
-                t = (char *) (symbs[AO.value]);
+                t = (symbols[AO.value].name);
             break;
         case GLOBALVAR_OT:
         case LOCALVAR_OT:
@@ -831,30 +850,30 @@ static void access_memory_g(int oc, assembly_operand AO1, assembly_operand AO2,
     {   
         size_ao = zero_ao; size_ao.value = -1;
         for (x=0; x<no_arrays; x++)
-        {   if (((AO1.marker == ARRAY_MV) == (!array_locs[x]))
-                && (AO1.value == svals[array_symbols[x]]))
-            {   size_ao.value = array_sizes[x]; y=x;
+        {   if (((AO1.marker == ARRAY_MV) == (!arrays[x].loc))
+                && (AO1.value == symbols[arrays[x].symbol].value))
+            {   size_ao.value = arrays[x].size; y=x;
             }
         }
         if (size_ao.value==-1) compiler_error("Array size can't be found");
 
-        type_ao = zero_ao; type_ao.value = array_types[y];
+        type_ao = zero_ao; type_ao.value = arrays[y].type;
 
-        if (array_locs[y] && !read_flag) {
+        if (arrays[y].loc && !read_flag) {
             error("Cannot write to a static array");
         }
 
         if ((!is_systemfile()))
         {   if (data_len == 1)
             {
-                if ((array_types[y] == WORD_ARRAY)
-                    || (array_types[y] == TABLE_ARRAY))
+                if ((arrays[y].type == WORD_ARRAY)
+                    || (arrays[y].type == TABLE_ARRAY))
                     warning("Using '->' to access a --> or table array");
             }
             else
             {
-                if ((array_types[y] == BYTE_ARRAY)
-                    || (array_types[y] == STRING_ARRAY))
+                if ((arrays[y].type == BYTE_ARRAY)
+                    || (arrays[y].type == STRING_ARRAY))
                  warning("Using '-->' to access a -> or string array");
             }
         }
@@ -876,25 +895,25 @@ static void access_memory_g(int oc, assembly_operand AO1, assembly_operand AO2,
            Here "size_ao.value" = largest permitted entry of its own kind */
         max_ao = size_ao;
         if (data_len == 1
-            && ((array_types[y] == WORD_ARRAY)
-                || (array_types[y] == TABLE_ARRAY)))
+            && ((arrays[y].type == WORD_ARRAY)
+                || (arrays[y].type == TABLE_ARRAY)))
         {   max_ao.value = size_ao.value*4 + 3;
             type_ao.value += 8;
         }
         if (data_len == 4
-            && ((array_types[y] == BYTE_ARRAY)
-                || (array_types[y] == STRING_ARRAY)
-                || (array_types[y] == BUFFER_ARRAY)))
+            && ((arrays[y].type == BYTE_ARRAY)
+                || (arrays[y].type == STRING_ARRAY)
+                || (arrays[y].type == BUFFER_ARRAY)))
         {   max_ao.value = (size_ao.value-3)/4;
             type_ao.value += 16;
         }
         max_ao.value++;
 
         /* Can't write to the size entry in a string or table */
-        if (((array_types[y] == STRING_ARRAY)
-             || (array_types[y] == TABLE_ARRAY))
+        if (((arrays[y].type == STRING_ARRAY)
+             || (arrays[y].type == TABLE_ARRAY))
             && (!read_flag))
-        {   if ((array_types[y] == TABLE_ARRAY) && data_len == 1)
+        {   if ((arrays[y].type == TABLE_ARRAY) && data_len == 1)
                 zero_ao.value = 4;
             else zero_ao.value = 1;
         }
@@ -995,7 +1014,8 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1,
   assembly_operand AO, AO2, AO3;
   int ln;
   int check_sp = FALSE, passed_label, failed_label, last_label;
-
+  int pre_unreach;
+  
   if (veneer_mode) 
     return AO1;
 
@@ -1017,6 +1037,8 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1,
     return AO1;
   }
 
+  pre_unreach = execution_never_reaches_here;
+  
   passed_label = next_label++;
   failed_label = next_label++;  
 
@@ -1037,6 +1059,8 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1,
     /* Allow classes */
     /* Test if zero... */
     assembleg_1_branch(jz_gc, AO, failed_label);
+    if (!pre_unreach && execution_never_reaches_here)
+        execution_never_reaches_here |= EXECSTATE_NOWARN;
     /* Test if first byte is 0x70... */
     assembleg_3(aloadb_gc, AO, zero_operand, stack_pointer);
     INITAO(&AO3);
@@ -1047,6 +1071,8 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1,
   else {
     /* Test if zero... */
     assembleg_1_branch(jz_gc, AO, failed_label);
+    if (!pre_unreach && execution_never_reaches_here)
+        execution_never_reaches_here |= EXECSTATE_NOWARN;
     /* Test if first byte is 0x70... */
     assembleg_3(aloadb_gc, AO, zero_operand, stack_pointer);
     INITAO(&AO3);
@@ -1057,7 +1083,7 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1,
     INITAOTV(&AO3, BYTECONSTANT_OT, GOBJFIELD_PARENT());
     assembleg_3(aload_gc, AO, AO3, stack_pointer);
     ln = symbol_index("Class", -1);
-    AO3.value = svals[ln];
+    AO3.value = symbols[ln].value;
     AO3.marker = OBJECT_MV;
     AO3.type = CONSTANT_OT;
     assembleg_2_branch(jne_gc, stack_pointer, AO3, passed_label);
@@ -1078,7 +1104,7 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1,
   else {
     /* Build the symbol for "Object" */
     ln = symbol_index("Object", -1);
-    AO2.value = svals[ln];
+    AO2.value = symbols[ln].value;
     AO2.marker = OBJECT_MV;
     AO2.type = CONSTANT_OT;
     if (check_sp) {
@@ -1116,6 +1142,8 @@ static void compile_conditional_g(condclass *cc,
       switch ((cc-condclasses)*2 + 500) {
 
       case HAS_CC:
+        check_warn_symbol_type(&AO1, OBJECT_T, 0, "\"has/hasnt\" expression");
+        check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"has/hasnt\" expression");
         if (runtime_error_checking_switch) {
           if (flag) 
             error_label = next_label++;
@@ -1194,6 +1222,8 @@ static void compile_conditional_g(condclass *cc,
         break;
 
       case IN_CC:
+        check_warn_symbol_type(&AO1, OBJECT_T, 0, "\"in/notin\" expression");
+        check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"in/notin\" expression");
         if (runtime_error_checking_switch) {
           if (flag) 
             error_label = next_label++;
@@ -1208,12 +1238,16 @@ static void compile_conditional_g(condclass *cc,
         break;
 
       case OFCLASS_CC:
+        /* first argument can be anything */
+        check_warn_symbol_type(&AO2, CLASS_T, 0, "\"ofclass\" expression");
         assembleg_call_2(veneer_routine(OC__Cl_VR), AO1, AO2, stack_pointer);
         the_zc = (flag ? jnz_gc : jz_gc);
         AO1 = stack_pointer;
         break;
 
       case PROVIDES_CC:
+        /* first argument can be anything */
+        check_warn_symbol_type(&AO2, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"provides\" expression");
         assembleg_call_2(veneer_routine(OP__Pr_VR), AO1, AO2, stack_pointer);
         the_zc = (flag ? jnz_gc : jz_gc);
         AO1 = stack_pointer;
@@ -1275,6 +1309,12 @@ static void generate_code_from(int n, int void_flag)
 
     if ((opnum == LOGAND_OP) || (opnum == LOGOR_OP))
     {   generate_code_from(below, FALSE);
+        if (execution_never_reaches_here) {
+            /* If the condition never falls through to here, then it
+               was an "... && 0 && ..." test. Our convention is to skip
+               the "not reached" warnings for this case. */
+            execution_never_reaches_here |= EXECSTATE_NOWARN;
+        }
         generate_code_from(ET[below].right, FALSE);
         goto OperatorGenerated;
     }
@@ -1685,6 +1725,8 @@ static void generate_code_from(int n, int void_flag)
 
         case PROP_ADD_OP:
              {   assembly_operand AO = ET[below].value;
+                 check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".&\" expression");
+                 check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".&\" expression");
                  if (runtime_error_checking_switch && (!veneer_mode))
                      AO = check_nonzero_at_runtime(AO, -1, PROP_ADD_RTE);
                  assemblez_2_to(get_prop_addr_zc, AO,
@@ -1695,6 +1737,8 @@ static void generate_code_from(int n, int void_flag)
 
         case PROP_NUM_OP:
              {   assembly_operand AO = ET[below].value;
+                 check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".#\" expression");
+                 check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".#\" expression");
                  if (runtime_error_checking_switch && (!veneer_mode))
                      AO = check_nonzero_at_runtime(AO, -1, PROP_NUM_RTE);
                  assemblez_2_to(get_prop_addr_zc, AO,
@@ -1707,49 +1751,70 @@ static void generate_code_from(int n, int void_flag)
              break;
 
         case PROPERTY_OP:
-             {   assembly_operand AO = ET[below].value;
-
+             {
+                 check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression");
+                 check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression");
                  if (runtime_error_checking_switch && (!veneer_mode))
                        assemblez_3_to(call_vs_zc, veneer_routine(RT__ChPR_VR),
-                         AO, ET[ET[below].right].value, temp_var1);
+                         ET[below].value, ET[ET[below].right].value, temp_var1);
                  else
-                 assemblez_2_to(get_prop_zc, AO,
+                 assemblez_2_to(get_prop_zc, ET[below].value,
                      ET[ET[below].right].value, temp_var1);
                  if (!void_flag) write_result_z(Result, temp_var1);
              }
              break;
 
         case MESSAGE_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression");
              j=1; AI.operand[0] = veneer_routine(RV__Pr_VR);
              goto GenFunctionCallZ;
         case MPROP_ADD_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".&\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".&\" expression");
              j=1; AI.operand[0] = veneer_routine(RA__Pr_VR);
              goto GenFunctionCallZ;
         case MPROP_NUM_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".#\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".#\" expression");
              j=1; AI.operand[0] = veneer_routine(RL__Pr_VR);
              goto GenFunctionCallZ;
         case MESSAGE_SETEQUALS_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression");
              j=1; AI.operand[0] = veneer_routine(WV__Pr_VR);
              goto GenFunctionCallZ;
         case MESSAGE_INC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"++.\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"++.\" expression");
              j=1; AI.operand[0] = veneer_routine(IB__Pr_VR);
              goto GenFunctionCallZ;
         case MESSAGE_DEC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"--.\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"--.\" expression");
              j=1; AI.operand[0] = veneer_routine(DB__Pr_VR);
              goto GenFunctionCallZ;
         case MESSAGE_POST_INC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".++\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".++\" expression");
              j=1; AI.operand[0] = veneer_routine(IA__Pr_VR);
              goto GenFunctionCallZ;
         case MESSAGE_POST_DEC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".--\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".--\" expression");
              j=1; AI.operand[0] = veneer_routine(DA__Pr_VR);
              goto GenFunctionCallZ;
         case SUPERCLASS_OP:
              j=1; AI.operand[0] = veneer_routine(RA__Sc_VR);
              goto GenFunctionCallZ;
         case PROP_CALL_OP:
+             check_warn_symbol_has_metaclass(&ET[below].value, "\".()\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".()\" expression");
              j=1; AI.operand[0] = veneer_routine(CA__Pr_VR);
              goto GenFunctionCallZ;
         case MESSAGE_CALL_OP:
+             check_warn_symbol_has_metaclass(&ET[below].value, "\".()\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".()\" expression");
              j=1; AI.operand[0] = veneer_routine(CA__Pr_VR);
              goto GenFunctionCallZ;
 
@@ -1853,6 +1918,7 @@ static void generate_code_from(int n, int void_flag)
 
                      case INDIRECT_SYSF:
                          j=0; i = ET[below].right;
+                         check_warn_symbol_type(&ET[i].value, ROUTINE_T, 0, "indirect function call");
                          goto IndirectFunctionCallZ;
 
                      case CHILDREN_SYSF:
@@ -1928,6 +1994,7 @@ static void generate_code_from(int n, int void_flag)
                  }
                  break;
              }
+             check_warn_symbol_type(&ET[below].value, ROUTINE_T, 0, "function call");
 
              GenFunctionCallZ:
 
@@ -1985,6 +2052,8 @@ static void generate_code_from(int n, int void_flag)
              break;
 
         case PROPERTY_SETEQUALS_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression");
              if (!void_flag)
              {   if (runtime_error_checking_switch)
                      assemblez_4_to(call_zc, veneer_routine(RT__ChPS_VR),
@@ -2128,6 +2197,8 @@ static void generate_code_from(int n, int void_flag)
              break;
 
         case PROPERTY_INC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"++.\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"++.\" expression");
              assemblez_store(temp_var1, ET[below].value);
              assemblez_store(temp_var2, ET[ET[below].right].value);
              assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3);
@@ -2140,6 +2211,8 @@ static void generate_code_from(int n, int void_flag)
              break;
 
         case PROPERTY_DEC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"--.\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"--.\" expression");
              assemblez_store(temp_var1, ET[below].value);
              assemblez_store(temp_var2, ET[ET[below].right].value);
              assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3);
@@ -2152,6 +2225,8 @@ static void generate_code_from(int n, int void_flag)
              break;
 
         case PROPERTY_POST_INC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".++\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".++\" expression");
              assemblez_store(temp_var1, ET[below].value);
              assemblez_store(temp_var2, ET[ET[below].right].value);
              assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3);
@@ -2164,6 +2239,8 @@ static void generate_code_from(int n, int void_flag)
              break;
 
         case PROPERTY_POST_DEC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".--\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".--\" expression");
              assemblez_store(temp_var1, ET[below].value);
              assemblez_store(temp_var2, ET[ET[below].right].value);
              assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3);
@@ -2181,7 +2258,7 @@ static void generate_code_from(int n, int void_flag)
             compiler_error("Expr code gen: Can't generate yet");
     }
   }
-  else {
+  else { /* Glulx */
     assembly_operand AO, AO2;
     if (operators[opnum].opcode_number_g != -1)
     {
@@ -2375,37 +2452,53 @@ static void generate_code_from(int n, int void_flag)
 
         case PROPERTY_OP:
         case MESSAGE_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression");
              AO = veneer_routine(RV__Pr_VR);
              goto TwoArgFunctionCall;
         case MPROP_ADD_OP:
         case PROP_ADD_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".&\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".&\" expression");
              AO = veneer_routine(RA__Pr_VR);
              goto TwoArgFunctionCall;
         case MPROP_NUM_OP:
         case PROP_NUM_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".#\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".#\" expression");
              AO = veneer_routine(RL__Pr_VR);
              goto TwoArgFunctionCall;
 
         case PROP_CALL_OP:
         case MESSAGE_CALL_OP:
+             check_warn_symbol_has_metaclass(&ET[below].value, "\".()\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".()\" expression");
              AO2 = veneer_routine(CA__Pr_VR);
              i = below;
              goto DoFunctionCall;
 
         case MESSAGE_INC_OP:
         case PROPERTY_INC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"++.\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"++.\" expression");
              AO = veneer_routine(IB__Pr_VR);
              goto TwoArgFunctionCall;
         case MESSAGE_DEC_OP:
         case PROPERTY_DEC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"--.\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"--.\" expression");
              AO = veneer_routine(DB__Pr_VR);
              goto TwoArgFunctionCall;
         case MESSAGE_POST_INC_OP:
         case PROPERTY_POST_INC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".++\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".++\" expression");
              AO = veneer_routine(IA__Pr_VR);
              goto TwoArgFunctionCall;
         case MESSAGE_POST_DEC_OP:
         case PROPERTY_POST_DEC_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".--\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".--\" expression");
              AO = veneer_routine(DA__Pr_VR);
              goto TwoArgFunctionCall;
         case SUPERCLASS_OP:
@@ -2425,6 +2518,8 @@ static void generate_code_from(int n, int void_flag)
 
         case PROPERTY_SETEQUALS_OP:
         case MESSAGE_SETEQUALS_OP:
+             check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression");
+             check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression");
              if (runtime_error_checking_switch && (!veneer_mode))
                  AO = veneer_routine(RT__ChPS_VR);
                else
@@ -2615,6 +2710,7 @@ static void generate_code_from(int n, int void_flag)
 
                      case INDIRECT_SYSF: 
                          i = ET[below].right;
+                         check_warn_symbol_type(&ET[i].value, ROUTINE_T, 0, "indirect function call");
                          goto IndirectFunctionCallG;
 
                      case GLK_SYSF: 
@@ -2684,6 +2780,7 @@ static void generate_code_from(int n, int void_flag)
                  break;
              }
 
+             check_warn_symbol_type(&ET[below].value, ROUTINE_T, 0, "function call");
              i = below;
 
              IndirectFunctionCallG:
@@ -2776,6 +2873,7 @@ static void generate_code_from(int n, int void_flag)
 
         if (ET[n].to_expression)
         {
+            int32 donelabel;
             if (void_flag) {
                 warning("Logical expression has no side-effects");
                 if (ET[n].true_label != -1)
@@ -2784,18 +2882,26 @@ static void generate_code_from(int n, int void_flag)
                     assemble_label_no(ET[n].false_label);
             }
             else if (ET[n].true_label != -1)
-            {   assemblez_1(push_zc, zero_operand);
-                assemblez_jump(next_label++);
+            {
+                donelabel = next_label++;
+                if (!execution_never_reaches_here) {
+                    assemblez_1(push_zc, zero_operand);
+                    assemblez_jump(donelabel);
+                }
                 assemble_label_no(ET[n].true_label);
                 assemblez_1(push_zc, one_operand);
-                assemble_label_no(next_label-1);
+                assemble_forward_label_no(donelabel);
             }
             else
-            {   assemblez_1(push_zc, one_operand);
-                assemblez_jump(next_label++);
+            {
+                donelabel = next_label++;
+                if (!execution_never_reaches_here) {
+                    assemblez_1(push_zc, one_operand);
+                    assemblez_jump(donelabel);
+                }
                 assemble_label_no(ET[n].false_label);
                 assemblez_1(push_zc, zero_operand);
-                assemble_label_no(next_label-1);
+                assemble_forward_label_no(donelabel);
             }
             ET[n].value = stack_pointer;
         }
@@ -2808,6 +2914,7 @@ static void generate_code_from(int n, int void_flag)
 
         if (ET[n].to_expression)
         {   
+            int32 donelabel;
             if (void_flag) {
                 warning("Logical expression has no side-effects");
                 if (ET[n].true_label != -1)
@@ -2816,18 +2923,26 @@ static void generate_code_from(int n, int void_flag)
                     assemble_label_no(ET[n].false_label);
             }
             else if (ET[n].true_label != -1)
-            {   assembleg_store(stack_pointer, zero_operand);
-                assembleg_jump(next_label++);
+            {
+                donelabel = next_label++;
+                if (!execution_never_reaches_here) {
+                    assembleg_store(stack_pointer, zero_operand);
+                    assembleg_jump(donelabel);
+                }
                 assemble_label_no(ET[n].true_label);
                 assembleg_store(stack_pointer, one_operand);
-                assemble_label_no(next_label-1);
+                assemble_forward_label_no(donelabel);
             }
             else
-            {   assembleg_store(stack_pointer, one_operand);
-                assembleg_jump(next_label++);
+            {
+                donelabel = next_label++;
+                if (!execution_never_reaches_here) {
+                    assembleg_store(stack_pointer, one_operand);
+                    assembleg_jump(donelabel);
+                }
                 assemble_label_no(ET[n].false_label);
                 assembleg_store(stack_pointer, zero_operand);
-                assemble_label_no(next_label-1);
+                assemble_forward_label_no(donelabel);
             }
             ET[n].value = stack_pointer;
         }
index 5b3c0a714f99104d565625a5450fe06fe8ab0018..0e0cf6f2773002c9b501d693cba43d5b2832ef0e 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "expressp" :  The expression parser                                     */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -65,10 +65,7 @@ static int comma_allowed, arrow_allowed, superclass_allowed,
            array_init_ambiguity, action_ambiguity,
            etoken_count, inserting_token, bracket_level;
 
-extern int *variable_usage;
-
-/* Must be at least as many as keyword_group system_functions (currently 12) */
-int system_function_usage[32];
+int system_function_usage[NUMBER_SYSTEM_FUNCTIONS];
 
 static int get_next_etoken(void)
 {   int v, symbol = 0, mark_symbol_as_used = FALSE,
@@ -86,6 +83,7 @@ static int get_next_etoken(void)
         current_token.value = token_value;
         current_token.type = token_type;
         current_token.marker = 0;
+        current_token.symindex = -1;
         current_token.symtype = 0;
         current_token.symflags = -1;
     }
@@ -93,7 +91,7 @@ static int get_next_etoken(void)
     switch(current_token.type)
     {   case LOCAL_VARIABLE_TT:
             current_token.type = VARIABLE_TT;
-            variable_usage[current_token.value] = TRUE;
+            variables[current_token.value].usage = TRUE;
             break;
 
         case DQ_TT:
@@ -136,15 +134,16 @@ but not used as a value:", unicode);
 
             mark_symbol_as_used = TRUE;
 
-            v = svals[symbol];
+            v = symbols[symbol].value;
 
-            current_token.symtype = stypes[symbol];
-            current_token.symflags = sflags[symbol];
-            switch(stypes[symbol])
+            current_token.symindex = symbol;
+            current_token.symtype = symbols[symbol].type;
+            current_token.symflags = symbols[symbol].flags;
+            switch(symbols[symbol].type)
             {   case ROUTINE_T:
                     /* Replaced functions must always be backpatched
                        because there could be another definition coming. */
-                    if (sflags[symbol] & REPLACE_SFLAG)
+                    if (symbols[symbol].flags & REPLACE_SFLAG)
                     {   current_token.marker = SYMBOL_MV;
                         if (module_switch) import_symbol(symbol);
                         v = symbol;
@@ -171,7 +170,7 @@ but not used as a value:", unicode);
                     if (module_switch) current_token.marker = IDENT_MV;
                     break;
                 case CONSTANT_T:
-                    if (sflags[symbol] & (UNKNOWN_SFLAG + CHANGE_SFLAG))
+                    if (symbols[symbol].flags & (UNKNOWN_SFLAG + CHANGE_SFLAG))
                     {   current_token.marker = SYMBOL_MV;
                         if (module_switch) import_symbol(symbol);
                         v = symbol;
@@ -185,7 +184,7 @@ but not used as a value:", unicode);
                     current_token.marker = 0;
                     break;
             }
-            if (sflags[symbol] & SYSTEM_SFLAG)
+            if (symbols[symbol].flags & SYSTEM_SFLAG)
                 current_token.marker = 0;
 
             current_token.value = v;
@@ -205,9 +204,9 @@ but not used as a value:", unicode);
                 else current_token.type = SMALL_NUMBER_TT;
             }
 
-            if (stypes[symbol] == GLOBAL_VARIABLE_T)
+            if (symbols[symbol].type == GLOBAL_VARIABLE_T)
             {   current_token.type = VARIABLE_TT;
-                variable_usage[current_token.value] = TRUE;
+                variables[current_token.value].usage = TRUE;
             }
             break;
 
@@ -329,7 +328,7 @@ but not used as a value:", unicode);
                     current_token.text += 3;
                     current_token.type = SYMBOL_TT;
                     symbol = symbol_index(current_token.text, -1);
-                    if (stypes[symbol] != GLOBAL_VARIABLE_T) {
+                    if (symbols[symbol].type != GLOBAL_VARIABLE_T) {
                         ebf_error(
                         "global variable name after '#g$'",
                         current_token.text);
@@ -339,7 +338,7 @@ but not used as a value:", unicode);
                         break;
                     }
                     mark_symbol_as_used = TRUE;
-                    current_token.value = svals[symbol] - MAX_LOCAL_VARIABLES;
+                    current_token.value = symbols[symbol].value - MAX_LOCAL_VARIABLES;
                     current_token.marker = 0;
                     if (!glulx_mode) {
                         if (current_token.value >= 0x100)
@@ -439,7 +438,7 @@ but not used as a value:", unicode);
     if (token_type_allowable[current_token.type]==0)
     {   if (expr_trace_level >= 3)
         {   printf("Discarding as not allowable: '%s' ", current_token.text);
-            describe_token(current_token);
+            describe_token(&current_token);
             printf("\n");
         }
         current_token.type = ENDEXP_TT;
@@ -454,16 +453,16 @@ but not used as a value:", unicode);
             || (operators[current_token.value].usage == PRE_U)))
     {   if (expr_trace_level >= 3)
         {   printf("Discarding as no longer part: '%s' ", current_token.text);
-            describe_token(current_token);
+            describe_token(&current_token);
             printf("\n");
         }
         current_token.type = ENDEXP_TT;
     }
     else
-    {   if (mark_symbol_as_used) sflags[symbol] |= USED_SFLAG;
+    {   if (mark_symbol_as_used) symbols[symbol].flags |= USED_SFLAG;
         if (expr_trace_level >= 3)
         {   printf("Expr token = '%s' ", current_token.text);
-            describe_token(current_token);
+            describe_token(&current_token);
             printf("\n");
         }
     }
@@ -500,7 +499,7 @@ const int prec_table[] = {
 
 };
 
-static int find_prec(token_data a, token_data b)
+static int find_prec(const token_data *a, const token_data *b)
 {
     /*  We are comparing the precedence of tokens  a  and  b
         (where a occurs to the left of b).  If the expression is correct,
@@ -518,14 +517,14 @@ static int find_prec(token_data a, token_data b)
 
     int i, j, l1, l2;
 
-    switch(a.type)
+    switch(a->type)
     {   case SUBOPEN_TT:  i=0; break;
         case SUBCLOSE_TT: i=1; break;
         case ENDEXP_TT:   i=2; break;
         case OP_TT:       i=3; break;
         default:          i=4; break;
     }
-    switch(b.type)
+    switch(b->type)
     {   case SUBOPEN_TT:  i+=0; break;
         case SUBCLOSE_TT: i+=5; break;
         case ENDEXP_TT:   i+=10; break;
@@ -535,10 +534,10 @@ static int find_prec(token_data a, token_data b)
 
     j = prec_table[i]; if (j != -1) return j;
 
-    l1 = operators[a.value].precedence;
-    l2 = operators[b.value].precedence;
-    if (operators[b.value].usage == PRE_U) return LOWER_P;
-    if (operators[a.value].usage == POST_U) return GREATER_P;
+    l1 = operators[a->value].precedence;
+    l2 = operators[b->value].precedence;
+    if (operators[b->value].usage == PRE_U) return LOWER_P;
+    if (operators[a->value].usage == POST_U) return GREATER_P;
 
     /*  Anomalous rule to resolve the function call precedence, which is
         different on the right from on the left, e.g., in:
@@ -551,7 +550,7 @@ static int find_prec(token_data a, token_data b)
 
     if (l1 < l2)  return LOWER_P;
     if (l1 > l2)  return GREATER_P;
-    switch(operators[a.value].associativity)
+    switch(operators[a->value].associativity)
     {   case L_A: return GREATER_P;
         case R_A: return LOWER_P;
         case 0:   return e5;
@@ -561,7 +560,8 @@ static int find_prec(token_data a, token_data b)
 
 /* --- Converting token to operand ----------------------------------------- */
 
-/* Must match the switch statement below */
+/* List used to generate gameinfo.dbg.
+   Must match the switch statement below. */
 int z_system_constant_list[] =
     { adjectives_table_SC,
       actions_table_SC,
@@ -605,6 +605,8 @@ int z_system_constant_list[] =
       highest_class_number_SC,
       class_objects_array_SC,
       highest_object_number_SC,
+      dictionary_table_SC,
+      grammar_table_SC,
       -1 };
 
 static int32 value_of_system_constant_z(int t)
@@ -642,9 +644,13 @@ static int32 value_of_system_constant_z(int t)
         case ipv__end_SC:
             return variables_offset;
         case array__start_SC:
-            return variables_offset + (MAX_GLOBAL_VARIABLES*WORDSIZE);
+            return variables_offset + (MAX_ZCODE_GLOBAL_VARS*WORDSIZE);
         case array__end_SC:
             return static_memory_offset;
+        case dictionary_table_SC:
+            return dictionary_offset;
+        case grammar_table_SC:
+            return static_memory_offset;
 
         case highest_attribute_number_SC:
             return no_attributes-1;
@@ -706,7 +712,8 @@ static int32 value_of_system_constant_z(int t)
     return(0);
 }
 
-/* Must match the switch statement below */
+/* List used to generate gameinfo.dbg.
+   Must match the switch statement below. */
 int glulx_system_constant_list[] =
     { classes_table_SC,
       identifiers_table_SC,
@@ -765,7 +772,15 @@ extern int32 value_of_system_constant(int t)
     return value_of_system_constant_g(t);    
 }
 
-static int evaluate_term(token_data t, assembly_operand *o)
+extern char *name_of_system_constant(int t)
+{
+  if (t < 0 || t >= NO_SYSTEM_CONSTANTS) {
+    return "???";
+  }
+  return system_constants.keywords[t];
+}
+
+static int evaluate_term(const token_data *t, assembly_operand *o)
 {
     /*  If the given token is a constant, evaluate it into the operand.
         For now, the identifiers are considered variables.
@@ -774,13 +789,12 @@ static int evaluate_term(token_data t, assembly_operand *o)
 
     int32 v;
 
-    o->marker = t.marker;
-    o->symtype = t.symtype;
-    o->symflags = t.symflags;
+    o->marker = t->marker;
+    o->symindex = t->symindex;
 
-    switch(t.type)
+    switch(t->type)
     {   case LARGE_NUMBER_TT:
-             v = t.value;
+             v = t->value;
              if (!glulx_mode) {
                  if (v < 0) v = v + 0x10000;
                  o->type = LONG_CONSTANT_OT;
@@ -792,7 +806,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
              }
              return(TRUE);
         case SMALL_NUMBER_TT:
-             v = t.value;
+             v = t->value;
              if (!glulx_mode) {
                  if (v < 0) v = v + 0x10000;
                  o->type = SHORT_CONSTANT_OT;
@@ -809,7 +823,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  o->type = LONG_CONSTANT_OT;
              else
                  o->type = CONSTANT_OT;
-             o->value = dictionary_add(t.text, 0x80, 0, 0);
+             o->value = dictionary_add(t->text, 0x80, 0, 0);
              return(TRUE);
         case DQ_TT:
              /*  Create as a static string  */
@@ -817,14 +831,14 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  o->type = LONG_CONSTANT_OT;
              else
                  o->type = CONSTANT_OT;
-             o->value = compile_string(t.text, STRCTX_GAME);
+             o->value = compile_string(t->text, STRCTX_GAME);
              return(TRUE);
         case VARIABLE_TT:
              if (!glulx_mode) {
                  o->type = VARIABLE_OT;
              }
              else {
-                 if (t.value >= MAX_LOCAL_VARIABLES) {
+                 if (t->value >= MAX_LOCAL_VARIABLES) {
                      o->type = GLOBALVAR_OT;
                  }
                  else {
@@ -833,21 +847,21 @@ static int evaluate_term(token_data t, assembly_operand *o)
                      o->type = LOCALVAR_OT;
                  }
              }
-             o->value = t.value;
+             o->value = t->value;
              return(TRUE);
         case SYSFUN_TT:
              if (!glulx_mode) {
                  o->type = VARIABLE_OT;
-                 o->value = t.value + 256;
+                 o->value = t->value + 256;
              }
              else {
                  o->type = SYSFUN_OT;
-                 o->value = t.value;
+                 o->value = t->value;
              }
-             system_function_usage[t.value] = 1;
+             system_function_usage[t->value] = 1;
              return(TRUE);
         case ACTION_TT:
-             *o = action_of_name(t.text);
+             *o = action_of_name(t->text);
              return(TRUE);
         case SYSTEM_CONSTANT_TT:
              /*  Certain system constants depend only on the
@@ -856,7 +870,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  them immediately.  */
              if (!glulx_mode) {
                  o->type = LONG_CONSTANT_OT;
-                 switch(t.value)
+                 switch(t->value)
                  {   
                  case version_number_SC:
                      o->type = SHORT_CONSTANT_OT;
@@ -873,6 +887,8 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  case dict_par3_SC:
                      o->type = SHORT_CONSTANT_OT;
                      o->marker = 0;
+                     if (ZCODE_LESS_DICT_DATA)
+                         error("#dict_par3 is unavailable when ZCODE_LESS_DICT_DATA is set");
                      v = (version_number==3)?6:8; break;
                  case lowest_attribute_number_SC:
                  case lowest_action_number_SC:
@@ -893,7 +909,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
                      o->type = SHORT_CONSTANT_OT; o->marker = 0;
                      v = oddeven_packing_switch; break;
                  default:
-                     v = t.value;
+                     v = t->value;
                      o->marker = INCON_MV;
                      break;
                  }
@@ -901,7 +917,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
              }
              else {
                  o->type = CONSTANT_OT;
-                 switch(t.value)
+                 switch(t->value)
                  {
                  /* The three dict_par flags point at the lower byte
                     of the flag field, because the library is written
@@ -943,7 +959,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  /* ###fix: need to fill more of these in! */
 
                  default:
-                     v = t.value;
+                     v = t->value;
                      o->marker = INCON_MV;
                      break;
                  }
@@ -957,35 +973,40 @@ static int evaluate_term(token_data t, assembly_operand *o)
 
 /* --- Emitter ------------------------------------------------------------- */
 
-expression_tree_node *ET;
+expression_tree_node *ET; /* Allocated to ET_used */
+static memory_list ET_memlist;
 static int ET_used;
 
 extern void clear_expression_space(void)
 {   ET_used = 0;
 }
 
-static assembly_operand *emitter_stack;
-static int *emitter_markers;
-static int *emitter_bracket_counts;
+typedef struct emitterstackinfo_s {
+    assembly_operand op;
+    int marker;
+    int bracket_count;
+} emitterstackinfo;
 
 #define FUNCTION_VALUE_MARKER 1
 #define ARGUMENT_VALUE_MARKER 2
 #define OR_VALUE_MARKER 3
 
+static emitterstackinfo *emitter_stack; /* Allocated to emitter_sp */
+static memory_list emitter_stack_memlist;
 static int emitter_sp;
 
 static int is_property_t(int symbol_type)
 {   return ((symbol_type == PROPERTY_T) || (symbol_type == INDIVIDUAL_PROPERTY_T));
 }
 
-static void mark_top_of_emitter_stack(int marker, token_data t)
+static void mark_top_of_emitter_stack(int marker, const token_data *t)
 {   if (emitter_sp < 1)
     {   compiler_error("SR error: Attempt to add a marker to the top of an empty emitter stack");
         return;
     }
     if (expr_trace_level >= 2)
     {   printf("Marking top of emitter stack (which is ");
-        print_operand(emitter_stack[emitter_sp-1]);
+        print_operand(&emitter_stack[emitter_sp-1].op, FALSE);
         printf(") as ");
         switch(marker)
         {
@@ -1004,21 +1025,20 @@ static void mark_top_of_emitter_stack(int marker, token_data t)
         }
         printf("\n");
     }
-    if (emitter_markers[emitter_sp-1])
+    if (emitter_stack[emitter_sp-1].marker)
     {   if (marker == ARGUMENT_VALUE_MARKER)
         {
             warning("Ignoring spurious leading comma");
             return;
         }
-        error_named("Missing operand for", t.text);
-        if (emitter_sp == MAX_EXPRESSION_NODES)
-            memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
-        emitter_markers[emitter_sp] = 0;
-        emitter_bracket_counts[emitter_sp] = 0;
-        emitter_stack[emitter_sp] = zero_operand;
+        error_named("Missing operand for", t->text);
+        ensure_memory_list_available(&emitter_stack_memlist, emitter_sp+1);
+        emitter_stack[emitter_sp].marker = 0;
+        emitter_stack[emitter_sp].bracket_count = 0;
+        emitter_stack[emitter_sp].op = zero_operand;
         emitter_sp++;
     }
-    emitter_markers[emitter_sp-1] = marker;
+    emitter_stack[emitter_sp-1].marker = marker;
 }
 
 static void add_bracket_layer_to_emitter_stack(int depth)
@@ -1026,7 +1046,7 @@ static void add_bracket_layer_to_emitter_stack(int depth)
     if (emitter_sp < depth + 1) return;
     if (expr_trace_level >= 2)
         printf("Adding bracket layer\n");
-    ++emitter_bracket_counts[emitter_sp-depth-1];
+    ++emitter_stack[emitter_sp-depth-1].bracket_count;
 }
 
 static void remove_bracket_layer_from_emitter_stack()
@@ -1034,46 +1054,47 @@ static void remove_bracket_layer_from_emitter_stack()
     if (emitter_sp < 2) return;
     if (expr_trace_level >= 2)
         printf("Removing bracket layer\n");
-    if (emitter_bracket_counts[emitter_sp-2] <= 0)
+    if (emitter_stack[emitter_sp-2].bracket_count <= 0)
     {   compiler_error("SR error: Attempt to remove a nonexistent bracket layer from the emitter stack");
         return;
     }
-    --emitter_bracket_counts[emitter_sp-2];
+    --emitter_stack[emitter_sp-2].bracket_count;
 }
 
-static void emit_token(token_data t)
+static void emit_token(const token_data *t)
 {   assembly_operand o1, o2; int arity, stack_size, i;
     int op_node_number, operand_node_number, previous_node_number;
     int32 x = 0;
 
     if (expr_trace_level >= 2)
-    {   printf("Output: %-19s%21s ", t.text, "");
+    {   printf("Output: %-19s%21s ", t->text, "");
         for (i=0; i<emitter_sp; i++)
-        {   print_operand(emitter_stack[i]); printf(" ");
-            if (emitter_markers[i] == FUNCTION_VALUE_MARKER) printf(":FUNCTION ");
-            if (emitter_markers[i] == ARGUMENT_VALUE_MARKER) printf(":ARGUMENT ");
-            if (emitter_markers[i] == OR_VALUE_MARKER) printf(":OR ");
-            if (emitter_bracket_counts[i]) printf(":BRACKETS(%d) ", emitter_bracket_counts[i]);
+        {   print_operand(&emitter_stack[i].op, FALSE); printf(" ");
+            if (emitter_stack[i].marker == FUNCTION_VALUE_MARKER) printf(":FUNCTION ");
+            if (emitter_stack[i].marker == ARGUMENT_VALUE_MARKER) printf(":ARGUMENT ");
+            if (emitter_stack[i].marker == OR_VALUE_MARKER) printf(":OR ");
+            if (emitter_stack[i].bracket_count) printf(":BRACKETS(%d) ", emitter_stack[i].bracket_count);
         }
         printf("\n");
     }
 
-    if (t.type == SUBOPEN_TT) return;
+    if (t->type == SUBOPEN_TT) return;
 
     stack_size = 0;
     while ((stack_size < emitter_sp) &&
-           !emitter_markers[emitter_sp-stack_size-1] &&
-           !emitter_bracket_counts[emitter_sp-stack_size-1])
+           !emitter_stack[emitter_sp-stack_size-1].marker &&
+           !emitter_stack[emitter_sp-stack_size-1].bracket_count)
         stack_size++;
 
-    if (t.type == SUBCLOSE_TT)
-    {   if (stack_size < emitter_sp && emitter_bracket_counts[emitter_sp-stack_size-1])
+    if (t->type == SUBCLOSE_TT)
+    {   if (stack_size < emitter_sp && emitter_stack[emitter_sp-stack_size-1].bracket_count)
         {   if (stack_size == 0)
             {   error("No expression between brackets '(' and ')'");
-                emitter_stack[emitter_sp] = zero_operand;
-                emitter_markers[emitter_sp] = 0;
-                emitter_bracket_counts[emitter_sp] = 0;
-                ++emitter_sp;
+                ensure_memory_list_available(&emitter_stack_memlist, emitter_sp+1);
+                emitter_stack[emitter_sp].op = zero_operand;
+                emitter_stack[emitter_sp].marker = 0;
+                emitter_stack[emitter_sp].bracket_count = 0;
+                emitter_sp++;
             }
             else if (stack_size < 1)
                 compiler_error("SR error: emitter stack empty in subexpression");
@@ -1084,14 +1105,14 @@ static void emit_token(token_data t)
         return;
     }
 
-    if (t.type != OP_TT)
-    {   emitter_markers[emitter_sp] = 0;
-        emitter_bracket_counts[emitter_sp] = 0;
+    if (t->type != OP_TT)
+    {
+        ensure_memory_list_available(&emitter_stack_memlist, emitter_sp+1);
+        emitter_stack[emitter_sp].marker = 0;
+        emitter_stack[emitter_sp].bracket_count = 0;
 
-        if (emitter_sp == MAX_EXPRESSION_NODES)
-            memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
-        if (!evaluate_term(t, &(emitter_stack[emitter_sp++])))
-            compiler_error_named("Emit token error:", t.text);
+        if (!evaluate_term(t, &(emitter_stack[emitter_sp++].op)))
+            compiler_error_named("Emit token error:", t->text);
         return;
     }
 
@@ -1099,85 +1120,85 @@ static void emit_token(token_data t)
        call, since we ignore spurious leading commas in function argument lists)
        with no intervening brackets.  Function calls are variadic, so we don't
        apply argument-separating commas. */
-    if (t.value == COMMA_OP &&
+    if (t->value == COMMA_OP &&
         stack_size < emitter_sp &&
-        (emitter_markers[emitter_sp-stack_size-1] == ARGUMENT_VALUE_MARKER ||
-         emitter_markers[emitter_sp-stack_size-1] == FUNCTION_VALUE_MARKER) &&
-        !emitter_bracket_counts[emitter_sp-stack_size-1])
+        (emitter_stack[emitter_sp-stack_size-1].marker == ARGUMENT_VALUE_MARKER ||
+         emitter_stack[emitter_sp-stack_size-1].marker == FUNCTION_VALUE_MARKER) &&
+        !emitter_stack[emitter_sp-stack_size-1].bracket_count)
     {   if (expr_trace_level >= 2)
             printf("Treating comma as argument-separating\n");
         return;
     }
 
-    if (t.value == OR_OP)
+    if (t->value == OR_OP)
         return;
 
     arity = 1;
-    if (t.value == FCALL_OP)
+    if (t->value == FCALL_OP)
     {   if (expr_trace_level >= 3)
         {   printf("FCALL_OP finds marker stack: ");
-            for (x=0; x<emitter_sp; x++) printf("%d ", emitter_markers[x]);
+            for (x=0; x<emitter_sp; x++) printf("%d ", emitter_stack[x].marker);
             printf("\n");
         }
-        if (emitter_markers[emitter_sp-1] == ARGUMENT_VALUE_MARKER)
+        if (emitter_stack[emitter_sp-1].marker == ARGUMENT_VALUE_MARKER)
             warning("Ignoring spurious trailing comma");
-        while (emitter_markers[emitter_sp-arity] != FUNCTION_VALUE_MARKER)
+        while (emitter_stack[emitter_sp-arity].marker != FUNCTION_VALUE_MARKER)
         {
             if ((glulx_mode &&
-                 emitter_stack[emitter_sp-arity].type == SYSFUN_OT) ||
+                 emitter_stack[emitter_sp-arity].op.type == SYSFUN_OT) ||
                 (!glulx_mode &&
-                 emitter_stack[emitter_sp-arity].type == VARIABLE_OT &&
-                 emitter_stack[emitter_sp-arity].value >= 256 &&
-                 emitter_stack[emitter_sp-arity].value < 288))
-            {   int index = emitter_stack[emitter_sp-arity].value;
+                 emitter_stack[emitter_sp-arity].op.type == VARIABLE_OT &&
+                 emitter_stack[emitter_sp-arity].op.value >= 256 &&
+                 emitter_stack[emitter_sp-arity].op.value < 288))
+            {   int index = emitter_stack[emitter_sp-arity].op.value;
                 if(!glulx_mode)
                     index -= 256;
                 if(index >= 0 && index < NUMBER_SYSTEM_FUNCTIONS)
                     error_named("System function name used as a value:", system_functions.keywords[index]);
                 else
                     compiler_error("Found unnamed system function used as a value");
-                emitter_stack[emitter_sp-arity] = zero_operand;
+                emitter_stack[emitter_sp-arity].op = zero_operand;
             }
             ++arity;
         }
     }
     else
     {   arity = 1;
-        if (operators[t.value].usage == IN_U) arity = 2;
+        if (operators[t->value].usage == IN_U) arity = 2;
 
-        if (operators[t.value].precedence == 3)
+        if (operators[t->value].precedence == 3)
         {   arity = 2;
             x = emitter_sp-1;
-            if(!emitter_markers[x] && !emitter_bracket_counts[x])
-            {   for (--x; emitter_markers[x] == OR_VALUE_MARKER && !emitter_bracket_counts[x]; --x)
+            if(!emitter_stack[x].marker && !emitter_stack[x].bracket_count)
+            {   for (--x; emitter_stack[x].marker == OR_VALUE_MARKER && !emitter_stack[x].bracket_count; --x)
                 {   ++arity;
                     ++stack_size;
                 }
-                for (;x >= 0 && !emitter_markers[x] && !emitter_bracket_counts[x]; --x)
+                for (;x >= 0 && !emitter_stack[x].marker && !emitter_stack[x].bracket_count; --x)
                     ++stack_size;
             }
         }
 
         if (arity > stack_size)
-        {   error_named("Missing operand for", t.text);
+        {   error_named("Missing operand for", t->text);
             while (arity > stack_size)
-            {   if (emitter_sp == MAX_EXPRESSION_NODES)
-                    memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
-                emitter_markers[emitter_sp] = 0;
-                emitter_bracket_counts[emitter_sp] = 0;
-                emitter_stack[emitter_sp] = zero_operand;
+            {   ensure_memory_list_available(&emitter_stack_memlist, emitter_sp+1);
+                emitter_stack[emitter_sp].marker = 0;
+                emitter_stack[emitter_sp].bracket_count = 0;
+                emitter_stack[emitter_sp].op = zero_operand;
                 emitter_sp++;
                 stack_size++;
             }
         }
     }
 
-    /* pseudo-typecheck in 6.30 */
+    /* pseudo-typecheck in 6.30: catch an unqualified property name */
     for (i = 1; i <= arity; i++)
     {
-        o1 = emitter_stack[emitter_sp - i];
-        if (is_property_t(o1.symtype) ) {
-            switch(t.value) 
+        o1 = emitter_stack[emitter_sp - i].op;
+        if ((o1.symindex >= 0)
+            && is_property_t(symbols[o1.symindex].type)) {
+            switch(t->value) 
             {
                 case FCALL_OP:
                 case SETEQUALS_OP: case NOTEQUAL_OP: 
@@ -1189,7 +1210,11 @@ static void emit_token(token_data t)
                 case PROPERTY_OP:
                     if (i < arity) break;
                 case GE_OP: case LE_OP:
-                    if ((i < arity) && (o1.symflags & STAR_SFLAG)) break;
+                    /* Direction properties "n_to", etc *are* compared
+                       in some libraries. They have STAR_SFLAG to tell us
+                       to skip the warning. */
+                    if ((i < arity)
+                        && (symbols[o1.symindex].flags & STAR_SFLAG)) break;
                 default:
                     warning("Property name in expression is not qualified by object");
             }
@@ -1198,9 +1223,9 @@ static void emit_token(token_data t)
 
     switch(arity)
     {   case 1:
-            o1 = emitter_stack[emitter_sp - 1];
+            o1 = emitter_stack[emitter_sp - 1].op;
             if ((o1.marker == 0) && is_constant_ot(o1.type))
-            {   switch(t.value)
+            {   switch(t->value)
                 {   case UNARY_MINUS_OP: x = -o1.value; goto FoldConstant;
                     case ARTNOT_OP: 
                          if (!glulx_mode)
@@ -1216,8 +1241,8 @@ static void emit_token(token_data t)
             break;
 
         case 2:
-            o1 = emitter_stack[emitter_sp - 2];
-            o2 = emitter_stack[emitter_sp - 1];
+            o1 = emitter_stack[emitter_sp - 2].op;
+            o2 = emitter_stack[emitter_sp - 1].op;
 
             if ((o1.marker == 0) && (o2.marker == 0)
                 && is_constant_ot(o1.type) && is_constant_ot(o2.type))
@@ -1232,7 +1257,7 @@ static void emit_token(token_data t)
                   ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value;
                 }
 
-                switch(t.value)
+                switch(t->value)
                 {
                     case PLUS_OP: x = ov1 + ov2; goto FoldConstantC;
                     case MINUS_OP: x = ov1 - ov2; goto FoldConstantC;
@@ -1242,7 +1267,7 @@ static void emit_token(token_data t)
                         if (ov2 == 0)
                           error("Division of constant by zero");
                         else
-                        if (t.value == DIVIDE_OP) {
+                        if (t->value == DIVIDE_OP) {
                           if (ov2 < 0) {
                             ov1 = -ov1;
                             ov2 = -ov2;
@@ -1291,13 +1316,32 @@ static void emit_token(token_data t)
                 }
 
             }
+
+            /* We can also fold logical operations if they are certain
+               to short-circuit. The right-hand argument is skipped even
+               if it's non-constant or has side effects. */
+            
+            if ((o1.marker == 0)
+                && is_constant_ot(o1.type)) {
+                
+                if (t->value == LOGAND_OP && o1.value == 0)
+                {
+                    x = 0;
+                    goto FoldConstant;
+                }
+
+                if (t->value == LOGOR_OP && o1.value != 0)
+                {
+                    x = 1;
+                    goto FoldConstant;
+                }
+            }
     }
 
+    ensure_memory_list_available(&ET_memlist, ET_used+1);
     op_node_number = ET_used++;
-    if (op_node_number == MAX_EXPRESSION_NODES)
-        memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
 
-    ET[op_node_number].operator_number = t.value;
+    ET[op_node_number].operator_number = t->value;
     ET[op_node_number].up = -1;
     ET[op_node_number].down = -1;
     ET[op_node_number].right = -1;
@@ -1311,14 +1355,14 @@ static void emit_token(token_data t)
         if (expr_trace_level >= 3)
             printf("i=%d, emitter_sp=%d, arity=%d, ETU=%d\n",
                 i, emitter_sp, arity, ET_used);
-        if (emitter_stack[i].type == EXPRESSION_OT)
-            operand_node_number = emitter_stack[i].value;
+        if (emitter_stack[i].op.type == EXPRESSION_OT)
+            operand_node_number = emitter_stack[i].op.value;
         else
-        {   operand_node_number = ET_used++;
-            if (operand_node_number == MAX_EXPRESSION_NODES)
-                memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+        {
+            ensure_memory_list_available(&ET_memlist, ET_used+1);
+            operand_node_number = ET_used++;
             ET[operand_node_number].down = -1;
-            ET[operand_node_number].value = emitter_stack[i];
+            ET[operand_node_number].value = emitter_stack[i].op;
         }
         ET[operand_node_number].up = op_node_number;
         ET[operand_node_number].right = -1;
@@ -1333,11 +1377,11 @@ static void emit_token(token_data t)
 
     emitter_sp = emitter_sp - arity + 1;
 
-    emitter_stack[emitter_sp - 1].type = EXPRESSION_OT;
-    emitter_stack[emitter_sp - 1].value = op_node_number;
+    emitter_stack[emitter_sp - 1].op.type = EXPRESSION_OT;
+    emitter_stack[emitter_sp - 1].op.value = op_node_number;
+    emitter_stack[emitter_sp - 1].op.marker = 0;
     emitter_stack[emitter_sp - 1].marker = 0;
-    emitter_markers[emitter_sp - 1] = 0;
-    emitter_bracket_counts[emitter_sp - 1] = 0;
+    emitter_stack[emitter_sp - 1].bracket_count = 0;
     /* Remove the marker for the brackets implied by operator precedence */
     remove_bracket_layer_from_emitter_stack();
 
@@ -1352,7 +1396,7 @@ static void emit_token(token_data t)
     {   char folding_error[40];
         int32 ov1 = (o1.value >= 0x8000) ? (o1.value - 0x10000) : o1.value;
         int32 ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value;
-        switch(t.value)
+        switch(t->value)
         {
             case PLUS_OP:
                 sprintf(folding_error, "%d + %d = %d", ov1, ov2, x);
@@ -1382,28 +1426,28 @@ the range -32768 to +32767:", folding_error);
 
     if (!glulx_mode) {
         if (x<256)
-            emitter_stack[emitter_sp - 1].type = SHORT_CONSTANT_OT;
-        else emitter_stack[emitter_sp - 1].type = LONG_CONSTANT_OT;
+            emitter_stack[emitter_sp - 1].op.type = SHORT_CONSTANT_OT;
+        else emitter_stack[emitter_sp - 1].op.type = LONG_CONSTANT_OT;
     }
     else {
         if (x == 0)
-            emitter_stack[emitter_sp - 1].type = ZEROCONSTANT_OT;
+            emitter_stack[emitter_sp - 1].op.type = ZEROCONSTANT_OT;
         else if (x >= -128 && x <= 127) 
-            emitter_stack[emitter_sp - 1].type = BYTECONSTANT_OT;
+            emitter_stack[emitter_sp - 1].op.type = BYTECONSTANT_OT;
         else if (x >= -32768 && x <= 32767) 
-            emitter_stack[emitter_sp - 1].type = HALFCONSTANT_OT;
+            emitter_stack[emitter_sp - 1].op.type = HALFCONSTANT_OT;
         else
-            emitter_stack[emitter_sp - 1].type = CONSTANT_OT;
+            emitter_stack[emitter_sp - 1].op.type = CONSTANT_OT;
     }
 
-    emitter_stack[emitter_sp - 1].value = x;
+    emitter_stack[emitter_sp - 1].op.value = x;
+    emitter_stack[emitter_sp - 1].op.marker = 0;
     emitter_stack[emitter_sp - 1].marker = 0;
-    emitter_markers[emitter_sp - 1] = 0;
-    emitter_bracket_counts[emitter_sp - 1] = 0;
+    emitter_stack[emitter_sp - 1].bracket_count = 0;
 
     if (expr_trace_level >= 2)
     {   printf("Folding constant to: ");
-        print_operand(emitter_stack[emitter_sp - 1]);
+        print_operand(&emitter_stack[emitter_sp - 1].op, FALSE);
         printf("\n");
     }
 
@@ -1419,9 +1463,7 @@ static void show_node(int n, int depth, int annotate)
     for (j=0; j<2*depth+2; j++) printf(" ");
 
     if (ET[n].down == -1)
-    {   print_operand(ET[n].value);
-        if (annotate && (ET[n].value.marker != 0))
-            printf(" [%s]", describe_mv(ET[n].value.marker));
+    {   print_operand(&ET[n].value, annotate);
         printf("\n");
     }
     else
@@ -1443,9 +1485,7 @@ static void show_node(int n, int depth, int annotate)
 extern void show_tree(assembly_operand AO, int annotate)
 {   if (AO.type == EXPRESSION_OT) show_node(AO.value, 0, annotate);
     else
-    {   printf("Constant: "); print_operand(AO);
-        if (annotate && (AO.marker != 0))
-            printf(" [%s]", describe_mv(AO.marker));
+    {   printf("Constant: "); print_operand(&AO, annotate);
         printf("\n");
     }
 }
@@ -1624,10 +1664,25 @@ static void delete_negations(int n, int context)
 
         (etc) to delete the ~~ operator from the tree.  Since this is
         depth first, the ~~ being deleted has no ~~s beneath it, which
-        is important to make "negate_condition" work.                        */
+        is important to make "negate_condition" work.
+
+        We also do the check for (x <= y or z) here. This must be done
+        before negate_condition.
+    */
 
     int i;
 
+    if (ET[n].operator_number == LE_OP || ET[n].operator_number == GE_OP) {
+        if (ET[n].down != -1
+            && ET[ET[n].down].right != -1
+            && ET[ET[ET[n].down].right].right != -1) {
+            if (ET[n].operator_number == LE_OP)
+                warning("The behavior of (<= or) may be unexpected.");
+            else
+                warning("The behavior of (>= or) may be unexpected.");
+        }
+    }
+
     if (ET[n].right != -1) delete_negations(ET[n].right, context);
     if (ET[n].down == -1) return;
     delete_negations(ET[n].down, context);
@@ -1654,9 +1709,9 @@ static void insert_exp_to_cond(int n, int context)
 
     if (ET[n].down == -1)
     {   if (context==CONDITION_CONTEXT)
-        {   new = ET_used++;
-            if (new == MAX_EXPRESSION_NODES)
-                memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+        {
+            ensure_memory_list_available(&ET_memlist, ET_used+1);
+            new = ET_used++;
             ET[new] = ET[n];
             ET[n].down = new; ET[n].operator_number = NONZERO_OP;
             ET[new].up = n; ET[new].right = -1;
@@ -1677,9 +1732,8 @@ static void insert_exp_to_cond(int n, int context)
         default:
             if (context != CONDITION_CONTEXT) break;
 
+            ensure_memory_list_available(&ET_memlist, ET_used+1);
             new = ET_used++;
-            if (new == MAX_EXPRESSION_NODES)
-                memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
             ET[new] = ET[n];
             ET[n].down = new; ET[n].operator_number = NONZERO_OP;
             ET[new].up = n; ET[new].right = -1;
@@ -1736,9 +1790,8 @@ static void func_args_on_stack(int n, int context)
               || ET[fnaddr].value.value == INDIRECT_SYSF
               || ET[fnaddr].value.value == GLK_SYSF))) {
         if (etoken_num_children(pn) > (unsigned int)(opnum == FCALL_OP ? 4:3)) {
+          ensure_memory_list_available(&ET_memlist, ET_used+1);
           new = ET_used++;
-          if (new == MAX_EXPRESSION_NODES)
-            memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
           ET[new] = ET[n];
           ET[n].down = new; 
           ET[n].operator_number = PUSH_OP;
@@ -1759,9 +1812,8 @@ static assembly_operand check_conditions(assembly_operand AO, int context)
 
     if (AO.type != EXPRESSION_OT)
     {   if (context != CONDITION_CONTEXT) return AO;
+        ensure_memory_list_available(&ET_memlist, ET_used+1);
         n = ET_used++;
-        if (n == MAX_EXPRESSION_NODES)
-            memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
         ET[n].down = -1;
         ET[n].up = -1;
         ET[n].right = -1;
@@ -1782,7 +1834,8 @@ static assembly_operand check_conditions(assembly_operand AO, int context)
 /* --- Shift-reduce parser ------------------------------------------------- */
 
 static int sr_sp;
-static token_data *sr_stack;
+static token_data *sr_stack; /* Allocated to sr_sp */
+static memory_list sr_stack_memlist;
 
 extern assembly_operand parse_expression(int context)
 {
@@ -1866,6 +1919,7 @@ extern assembly_operand parse_expression(int context)
     previous_token.type = ENDEXP_TT;
     previous_token.value = 0;
 
+    ensure_memory_list_available(&sr_stack_memlist, 1);
     sr_sp = 1;
     sr_stack[0] = previous_token;
 
@@ -1905,7 +1959,7 @@ extern assembly_operand parse_expression(int context)
                 return AO;
             }
 
-            AO = emitter_stack[0];
+            AO = emitter_stack[0].op;
             if (AO.type == EXPRESSION_OT)
             {   if (expr_trace_level >= 3)
                 {   printf("Tree before lvalue checking:\n");
@@ -1917,7 +1971,9 @@ extern assembly_operand parse_expression(int context)
                 ET[AO.value].up = -1;
             }
             else {
-                if ((context != CONSTANT_CONTEXT) && is_property_t(AO.symtype) 
+                if ((context != CONSTANT_CONTEXT)
+                    && (AO.symindex >= 0)
+                    && is_property_t(symbols[AO.symindex].type) 
                     && (arrow_allowed) && (!bare_prop_allowed))
                     warning("Bare property name found. \"self.prop\" intended?");
             }
@@ -1934,7 +1990,7 @@ extern assembly_operand parse_expression(int context)
             return(AO);
         }
 
-        switch(find_prec(a,b))
+        switch(find_prec(&a,&b))
         {
             case e5:                 /* Associativity error                  */
                 error_named("Brackets mandatory to clarify order of:",
@@ -1942,14 +1998,13 @@ extern assembly_operand parse_expression(int context)
 
             case LOWER_P:
             case EQUAL_P:
-                if (sr_sp == MAX_EXPRESSION_NODES)
-                    memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+                ensure_memory_list_available(&sr_stack_memlist, sr_sp+1);
                 sr_stack[sr_sp++] = b;
                 switch(b.type)
                 {
                     case SUBOPEN_TT:
                         if (sr_sp >= 2 && sr_stack[sr_sp-2].type == OP_TT && sr_stack[sr_sp-2].value == FCALL_OP)
-                            mark_top_of_emitter_stack(FUNCTION_VALUE_MARKER, b);
+                            mark_top_of_emitter_stack(FUNCTION_VALUE_MARKER, &b);
                         else
                             add_bracket_layer_to_emitter_stack(0);
                         break;
@@ -1958,7 +2013,7 @@ extern assembly_operand parse_expression(int context)
                             case OR_OP:
                                 if (sr_stack[sr_sp-2].type == OP_TT &&
                                     operators[sr_stack[sr_sp-2].value].precedence == 3)
-                                    mark_top_of_emitter_stack(OR_VALUE_MARKER, b);
+                                    mark_top_of_emitter_stack(OR_VALUE_MARKER, &b);
                                 else
                                 {   error("'or' not between values to the right of a condition");
                                     /* Convert to + for error recovery purposes */
@@ -1974,7 +2029,7 @@ extern assembly_operand parse_expression(int context)
                                     if (shallowest_open_bracket_index > 0 &&
                                         sr_stack[shallowest_open_bracket_index-1].type == OP_TT &&
                                         sr_stack[shallowest_open_bracket_index-1].value == FCALL_OP)
-                                    {   mark_top_of_emitter_stack(ARGUMENT_VALUE_MARKER, b);
+                                    {   mark_top_of_emitter_stack(ARGUMENT_VALUE_MARKER, &b);
                                         break;
                                     }
                                     /* Non-argument-separating commas get treated like any other operator; we fall through to the default case. */
@@ -1992,9 +2047,9 @@ extern assembly_operand parse_expression(int context)
             case GREATER_P:
                 do
                 {   pop = sr_stack[sr_sp - 1];
-                    emit_token(pop);
+                    emit_token(&pop);
                     sr_sp--;
-                } while (find_prec(sr_stack[sr_sp-1], pop) != LOWER_P);
+                } while (find_prec(&sr_stack[sr_sp-1], &pop) != LOWER_P);
                 break;
 
             case e1:                 /* Missing operand error                */
@@ -2056,7 +2111,12 @@ extern void init_expressp_vars(void)
 {   int i;
     /* make_operands(); */
     make_lexical_interface_tables();
-    for (i=0;i<32;i++) system_function_usage[i] = 0;
+    for (i=0; i<NUMBER_SYSTEM_FUNCTIONS; i++)
+        system_function_usage[i] = 0;
+
+    ET = NULL;
+    emitter_stack = NULL;
+    sr_stack = NULL;
 }
 
 extern void expressp_begin_pass(void)
@@ -2064,24 +2124,27 @@ extern void expressp_begin_pass(void)
 }
 
 extern void expressp_allocate_arrays(void)
-{   ET = my_calloc(sizeof(expression_tree_node), MAX_EXPRESSION_NODES,
+{
+    initialise_memory_list(&ET_memlist,
+        sizeof(expression_tree_node), 100, (void**)&ET,
         "expression parse trees");
-    emitter_markers = my_calloc(sizeof(int), MAX_EXPRESSION_NODES,
-        "emitter markers");
-    emitter_bracket_counts = my_calloc(sizeof(int), MAX_EXPRESSION_NODES,
-        "emitter bracket layer counts");
-    emitter_stack = my_calloc(sizeof(assembly_operand), MAX_EXPRESSION_NODES,
-        "emitter stack");
-    sr_stack = my_calloc(sizeof(token_data), MAX_EXPRESSION_NODES,
+
+    initialise_memory_list(&emitter_stack_memlist,
+        sizeof(emitterstackinfo), 100, (void**)&emitter_stack,
+        "expression stack");
+
+    initialise_memory_list(&sr_stack_memlist,
+        sizeof(token_data), 100, (void**)&sr_stack,
         "shift-reduce parser stack");
 }
 
 extern void expressp_free_arrays(void)
-{   my_free(&ET, "expression parse trees");
-    my_free(&emitter_markers, "emitter markers");
-    my_free(&emitter_bracket_counts, "emitter bracket layer counts");
-    my_free(&emitter_stack, "emitter stack");
-    my_free(&sr_stack, "shift-reduce parser stack");
+{
+    deallocate_memory_list(&ET_memlist);
+    
+    deallocate_memory_list(&emitter_stack_memlist);
+
+    deallocate_memory_list(&sr_stack_memlist);
 }
 
 /* ========================================================================= */
index b027dd40f97916e74b90e2a8d28bf2279fa72189..06e85d07031e821b8abc1de0ec7a8c94bae3ab0e 100644 (file)
@@ -7,8 +7,8 @@
 /*             routines in "inform.c", since they are tied up with ICL       */
 /*             settings and are very host OS-dependent.                      */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -48,10 +48,9 @@ static int checksum_count;              /* similarly                         */
 /*   level is only concerned with file names and handles.                    */
 /* ------------------------------------------------------------------------- */
 
-FileId *InputFiles=NULL;                /*  Ids for all the source files     */
-static char *filename_storage,          /*  Translated filenames             */
-            *filename_storage_p;
-static int filename_storage_left;
+FileId *InputFiles=NULL;                /*  Ids for all the source files
+                                            Allocated to total_files         */
+static memory_list InputFiles_memlist;
 
 /* ------------------------------------------------------------------------- */
 /*   When emitting debug information, we won't have addresses of routines,   */
@@ -87,13 +86,6 @@ static debug_backpatch_accumulator global_backpatch_accumulator;
 static debug_backpatch_accumulator array_backpatch_accumulator;
 static debug_backpatch_accumulator grammar_backpatch_accumulator;
 
-/* ------------------------------------------------------------------------- */
-/*   File handles and names for temporary files.                             */
-/* ------------------------------------------------------------------------- */
-
-FILE *Temp1_fp=NULL, *Temp2_fp=NULL,  *Temp3_fp=NULL;
-char Temp1_Name[PATHLEN], Temp2_Name[PATHLEN], Temp3_Name[PATHLEN];
-
 /* ------------------------------------------------------------------------- */
 /*   Opening and closing source code files                                   */
 /* ------------------------------------------------------------------------- */
@@ -118,8 +110,7 @@ extern void load_sourcefile(char *filename_given, int same_directory_flag)
     int x = 0;
     FILE *handle;
 
-    if (total_files == MAX_SOURCE_FILES)
-        memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES);
+    ensure_memory_list_available(&InputFiles_memlist, total_files+1);
 
     do
     {   x = translate_in_filename(x, name, filename_given, same_directory_flag,
@@ -127,14 +118,8 @@ extern void load_sourcefile(char *filename_given, int same_directory_flag)
         handle = fopen(name,"r");
     } while ((handle == NULL) && (x != 0));
 
-    if (filename_storage_left <= (int)strlen(name))
-        memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES);
-
-    filename_storage_left -= strlen(name)+1;
-    strcpy(filename_storage_p, name);
-    InputFiles[total_files].filename = filename_storage_p;
-
-    filename_storage_p += strlen(name)+1;
+    InputFiles[total_files].filename = my_malloc(strlen(name)+1, "filename storage");
+    strcpy(InputFiles[total_files].filename, name);
 
     if (debugfile_switch)
     {   debug_file_printf("<source index=\"%d\">", total_files);
@@ -157,8 +142,10 @@ extern void load_sourcefile(char *filename_given, int same_directory_flag)
         fatalerror_named("Couldn't open source file", name);
 
     InputFiles[total_files].is_input = TRUE;
+    InputFiles[total_files].initial_buffering = TRUE;
 
-    if (line_trace_level > 0) printf("\nOpening file \"%s\"\n",name);
+    if (files_trace_setting > 0)
+        printf("Opening file \"%s\"\n",name);
 
     total_files++;
     total_input_files++;
@@ -169,7 +156,8 @@ static void close_sourcefile(int file_number)
 {
     if (InputFiles[file_number-1].handle == NULL) return;
 
-    /*  Close this file.  */
+    /*  Close this file. But keep the InputFiles entry around, including
+        its filename. */
 
     if (ferror(InputFiles[file_number-1].handle))
         fatalerror_named("I/O failure: couldn't read from source file",
@@ -179,7 +167,10 @@ static void close_sourcefile(int file_number)
 
     InputFiles[file_number-1].handle = NULL;
 
-    if (line_trace_level > 0) printf("\nClosing file\n");
+    if (files_trace_setting > 0) {
+        char *str = (InputFiles[file_number-1].initial_buffering ? " (in initial buffering)" : "");
+        printf("Closing file \"%s\"%s\n", InputFiles[file_number-1].filename, str);
+    }
 }
 
 extern void close_all_source(void)
@@ -219,17 +210,10 @@ extern int register_orig_sourcefile(char *filename)
 
     name = filename; /* no translation */
 
-    if (total_files == MAX_SOURCE_FILES)
-        memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES);
-
-    if (filename_storage_left <= (int)strlen(name))
-        memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES);
-
-    filename_storage_left -= strlen(name)+1;
-    strcpy(filename_storage_p, name);
-    InputFiles[total_files].filename = filename_storage_p;
+    ensure_memory_list_available(&InputFiles_memlist, total_files+1);
 
-    filename_storage_p += strlen(name)+1;
+    InputFiles[total_files].filename = my_malloc(strlen(name)+1, "filename storage");
+    strcpy(InputFiles[total_files].filename, name);
 
     if (debugfile_switch)
     {   debug_file_printf("<source index=\"%d\">", total_files);
@@ -242,6 +226,7 @@ extern int register_orig_sourcefile(char *filename)
 
     InputFiles[total_files].handle = NULL;
     InputFiles[total_files].is_input = FALSE;
+    InputFiles[total_files].initial_buffering = FALSE;
 
     total_files++;
     current_origsource_file = total_files;
@@ -403,7 +388,7 @@ static void output_compression(int entnum, int32 *size, int *count)
 }
 
 static void output_file_z(void)
-{   FILE *fin=NULL; char new_name[PATHLEN];
+{   char new_name[PATHLEN];
     int32 length, blanks=0, size, i, j, offset;
     uint32 code_length, size_before_code, next_cons_check;
     int use_function;
@@ -457,14 +442,6 @@ static void output_file_z(void)
 
     /*  (2)  Output the compiled code area.                                  */
 
-    if (temporary_files_switch)
-    {   fclose(Temp2_fp);
-        Temp2_fp = NULL;
-        fin=fopen(Temp2_Name,"rb");
-        if (fin==NULL)
-            fatalerror("I/O failure: couldn't reopen temporary file 2");
-    }
-
     if (!OMIT_UNUSED_ROUTINES) {
         /* This is the old-fashioned case, which is easy. All of zcode_area
            (zmachine_pc bytes) will be output. next_cons_check will be
@@ -495,11 +472,11 @@ static void output_file_z(void)
     for (i=0; i<zcode_backpatch_size; i=i+3)
     {   int long_flag = TRUE;
         offset
-            = 256*read_byte_from_memory_block(&zcode_backpatch_table, i+1)
-              + read_byte_from_memory_block(&zcode_backpatch_table, i+2);
+            = 256*zcode_backpatch_table[i+1]
+              + zcode_backpatch_table[i+2];
         backpatch_error_flag = FALSE;
         backpatch_marker
-            = read_byte_from_memory_block(&zcode_backpatch_table, i);
+            = zcode_backpatch_table[i];
         if (backpatch_marker >= 0x80) long_flag = FALSE;
         backpatch_marker &= 0x7f;
         offset = offset + (backpatch_marker/32)*0x10000;
@@ -514,17 +491,13 @@ static void output_file_z(void)
         while (j<offset) {
             if (!use_function) {
                 while (j<offset && j<next_cons_check) {
-                    /* get dummy value */
-                    ((temporary_files_switch)?fgetc(fin):
-                        read_byte_from_memory_block(&zcode_area, j));
                     j++;
                 }
             }
             else {
                 while (j<offset && j<next_cons_check) {
                     size++;
-                    sf_put((temporary_files_switch)?fgetc(fin):
-                        read_byte_from_memory_block(&zcode_area, j));
+                    sf_put(zcode_area[j]);
                     j++;
                 }
             }
@@ -533,10 +506,8 @@ static void output_file_z(void)
         }
 
         if (long_flag)
-        {   int32 v = (temporary_files_switch)?fgetc(fin):
-                read_byte_from_memory_block(&zcode_area, j);
-            v = 256*v + ((temporary_files_switch)?fgetc(fin):
-                read_byte_from_memory_block(&zcode_area, j+1));
+        {   int32 v = zcode_area[j];
+            v = 256*v + (zcode_area[j+1]);
             j += 2;
             if (use_function) {
                 v = backpatch_value(v);
@@ -545,8 +516,7 @@ static void output_file_z(void)
             }
         }
         else
-        {   int32 v = (temporary_files_switch)?fgetc(fin):
-                read_byte_from_memory_block(&zcode_area, j);
+        {   int32 v = zcode_area[j];
             j++;
             if (use_function) {
                 v = backpatch_value(v);
@@ -570,17 +540,13 @@ static void output_file_z(void)
     while (j<offset) {
         if (!use_function) {
             while (j<offset && j<next_cons_check) {
-                /* get dummy value */
-                ((temporary_files_switch)?fgetc(fin):
-                    read_byte_from_memory_block(&zcode_area, j));
                 j++;
             }
         }
         else {
             while (j<offset && j<next_cons_check) {
                 size++;
-                sf_put((temporary_files_switch)?fgetc(fin):
-                    read_byte_from_memory_block(&zcode_area, j));
+                sf_put(zcode_area[j]);
                 j++;
             }
         }
@@ -588,13 +554,6 @@ static void output_file_z(void)
             next_cons_check = df_next_function_iterate(&use_function);
     }
 
-    if (temporary_files_switch)
-    {   if (ferror(fin))
-            fatalerror("I/O failure: couldn't read from temporary file 2");
-        fclose(fin);
-        fin = NULL;
-    }
-
     if (size_before_code + code_length != size)
         compiler_error("Code output length did not match");
 
@@ -605,52 +564,22 @@ static void output_file_z(void)
 
     /*  (4)  Output the static strings area.                                 */
 
-    if (temporary_files_switch)
-    {   fclose(Temp1_fp);
-        Temp1_fp = NULL;
-        fin=fopen(Temp1_Name,"rb");
-        if (fin==NULL)
-            fatalerror("I/O failure: couldn't reopen temporary file 1");
-        for (i=0; i<static_strings_extent; i++) sf_put(fgetc(fin));
-        if (ferror(fin))
-            fatalerror("I/O failure: couldn't read from temporary file 1");
-        fclose(fin);
-        fin = NULL;
-        remove(Temp1_Name); remove(Temp2_Name);
-    }
-    else
-      for (i=0; i<static_strings_extent; i++) {
-        sf_put(read_byte_from_memory_block(&static_strings_area,i));
+    for (i=0; i<static_strings_extent; i++) {
+        sf_put(static_strings_area[i]);
         size++;
-      }
+    }
 
     /*  (5)  Output the linking data table (in the case of a module).        */
 
-    if (temporary_files_switch)
-    {   if (module_switch)
-        {   fclose(Temp3_fp);
-            Temp3_fp = NULL;
-            fin=fopen(Temp3_Name,"rb");
-            if (fin==NULL)
-                fatalerror("I/O failure: couldn't reopen temporary file 3");
-            for (j=0; j<link_data_size; j++) sf_put(fgetc(fin));
-            if (ferror(fin))
-                fatalerror("I/O failure: couldn't read from temporary file 3");
-            fclose(fin);
-            fin = NULL;
-            remove(Temp3_Name);
-        }
-    }
-    else
-        if (module_switch)
-            for (i=0; i<link_data_size; i++)
-                sf_put(read_byte_from_memory_block(&link_data_area,i));
+    if (module_switch)
+        for (i=0; i<link_data_size; i++)
+            sf_put(link_data_area[i]);
 
     if (module_switch)
     {   for (i=0; i<zcode_backpatch_size; i++)
-            sf_put(read_byte_from_memory_block(&zcode_backpatch_table, i));
+            sf_put(zcode_backpatch_table[i]);
         for (i=0; i<zmachine_backpatch_size; i++)
-            sf_put(read_byte_from_memory_block(&zmachine_backpatch_table, i));
+            sf_put(zmachine_backpatch_table[i]);
     }
 
     /*  (6)  Output null bytes to reach a multiple of 0.5K.                  */
@@ -708,7 +637,7 @@ static void output_file_z(void)
 }
 
 static void output_file_g(void)
-{   FILE *fin=NULL; char new_name[PATHLEN];
+{   char new_name[PATHLEN];
     int32 size, i, j, offset;
     int32 VersionNum;
     uint32 code_length, size_before_code, next_cons_check;
@@ -752,6 +681,9 @@ static void output_file_g(void)
     if (uses_float_features) {
       VersionNum = 0x00030102;
     }
+    if (uses_extundo_features) {
+      VersionNum = 0x00030103;
+    }
 
     /* And check if the user has requested a specific version. */
     if (requested_glulx_version) {
@@ -850,14 +782,6 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
 
     /*  (2)  Output the compiled code area. */
 
-    if (temporary_files_switch)
-    {   fclose(Temp2_fp);
-        Temp2_fp = NULL;
-        fin=fopen(Temp2_Name,"rb");
-        if (fin==NULL)
-            fatalerror("I/O failure: couldn't reopen temporary file 2");
-    }
-
     if (!OMIT_UNUSED_ROUTINES) {
         /* This is the old-fashioned case, which is easy. All of zcode_area
            (zmachine_pc bytes) will be output. next_cons_check will be
@@ -889,32 +813,28 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
         int data_len;
         int32 v;
         offset = 
-          (read_byte_from_memory_block(&zcode_backpatch_table, i+2) << 24)
-          | (read_byte_from_memory_block(&zcode_backpatch_table, i+3) << 16)
-          | (read_byte_from_memory_block(&zcode_backpatch_table, i+4) << 8)
-          | (read_byte_from_memory_block(&zcode_backpatch_table, i+5));
+          (zcode_backpatch_table[i+2] << 24)
+          | (zcode_backpatch_table[i+3] << 16)
+          | (zcode_backpatch_table[i+4] << 8)
+          | (zcode_backpatch_table[i+5]);
         backpatch_error_flag = FALSE;
         backpatch_marker =
-          read_byte_from_memory_block(&zcode_backpatch_table, i);
+          zcode_backpatch_table[i];
         data_len =
-          read_byte_from_memory_block(&zcode_backpatch_table, i+1);
+          zcode_backpatch_table[i+1];
 
         /* All code up until the next backpatch marker gets flushed out
            as-is. (Unless we're in a stripped-out function.) */
         while (j<offset) {
             if (!use_function) {
                 while (j<offset && j<next_cons_check) {
-                    /* get dummy value */
-                    ((temporary_files_switch)?fgetc(fin):
-                        read_byte_from_memory_block(&zcode_area, j));
                     j++;
                 }
             }
             else {
                 while (j<offset && j<next_cons_check) {
                     size++;
-                    sf_put((temporary_files_switch)?fgetc(fin):
-                        read_byte_from_memory_block(&zcode_area, j));
+                    sf_put(zcode_area[j]);
                     j++;
                 }
             }
@@ -927,14 +847,10 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
         switch (data_len) {
 
         case 4:
-          v = ((temporary_files_switch)?fgetc(fin):
-            read_byte_from_memory_block(&zcode_area, j));
-          v = (v << 8) | ((temporary_files_switch)?fgetc(fin):
-            read_byte_from_memory_block(&zcode_area, j+1));
-          v = (v << 8) | ((temporary_files_switch)?fgetc(fin):
-            read_byte_from_memory_block(&zcode_area, j+2));
-          v = (v << 8) | ((temporary_files_switch)?fgetc(fin):
-            read_byte_from_memory_block(&zcode_area, j+3));
+          v = (zcode_area[j]);
+          v = (v << 8) | (zcode_area[j+1]);
+          v = (v << 8) | (zcode_area[j+2]);
+          v = (v << 8) | (zcode_area[j+3]);
           j += 4;
           if (!use_function)
               break;
@@ -947,10 +863,8 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
           break;
 
         case 2:
-          v = ((temporary_files_switch)?fgetc(fin):
-            read_byte_from_memory_block(&zcode_area, j));
-          v = (v << 8) | ((temporary_files_switch)?fgetc(fin):
-            read_byte_from_memory_block(&zcode_area, j+1));
+          v = (zcode_area[j]);
+          v = (v << 8) | (zcode_area[j+1]);
           j += 2;
           if (!use_function)
               break;
@@ -965,8 +879,7 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
           break;
 
         case 1:
-          v = ((temporary_files_switch)?fgetc(fin):
-            read_byte_from_memory_block(&zcode_area, j));
+          v = (zcode_area[j]);
           j += 1;
           if (!use_function)
               break;
@@ -1000,17 +913,13 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
     while (j<offset) {
         if (!use_function) {
             while (j<offset && j<next_cons_check) {
-                /* get dummy value */
-                ((temporary_files_switch)?fgetc(fin):
-                    read_byte_from_memory_block(&zcode_area, j));
                 j++;
             }
         }
         else {
             while (j<offset && j<next_cons_check) {
                 size++;
-                sf_put((temporary_files_switch)?fgetc(fin):
-                    read_byte_from_memory_block(&zcode_area, j));
+                sf_put(zcode_area[j]);
                 j++;
             }
         }
@@ -1018,21 +927,11 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
             next_cons_check = df_next_function_iterate(&use_function);
     }
 
-    if (temporary_files_switch)
-    {   if (ferror(fin))
-            fatalerror("I/O failure: couldn't read from temporary file 2");
-        fclose(fin);
-        fin = NULL;
-    }
-
     if (size_before_code + code_length != size)
         compiler_error("Code output length did not match");
 
     /*  (4)  Output the static strings area.                                 */
 
-    if (temporary_files_switch) {
-      fseek(Temp1_fp, 0, SEEK_SET);
-    }
     {
       int32 ix, lx;
       int ch, jx, curbyte, bx;
@@ -1086,10 +985,7 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
         jx = 0; 
         curbyte = 0;
         while (!done) {
-          if (temporary_files_switch)
-            ch = fgetc(Temp1_fp);
-          else
-            ch = read_byte_from_memory_block(&static_strings_area, ix);
+          ch = static_strings_area[ix];
           ix++;
           if (ix > static_strings_extent || ch < 0)
             compiler_error("Read too much not-yet-compressed text.");
@@ -1191,13 +1087,13 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver
         int32 val, ix, jx;
         for (ix=0, jx=0; ix<staticarray_backpatch_size; ix += 5) {
             backpatch_error_flag = FALSE;
-            backpatch_marker = read_byte_from_memory_block(&staticarray_backpatch_table, ix);
+            backpatch_marker = staticarray_backpatch_table[ix];
             /* datalen is always 4 for array backpatching */
             offset = 
-                (read_byte_from_memory_block(&staticarray_backpatch_table, ix+1) << 24)
-                | (read_byte_from_memory_block(&staticarray_backpatch_table, ix+2) << 16)
-                | (read_byte_from_memory_block(&staticarray_backpatch_table, ix+3) << 8)
-                | (read_byte_from_memory_block(&staticarray_backpatch_table, ix+4));
+                (staticarray_backpatch_table[ix+1] << 24)
+                | (staticarray_backpatch_table[ix+2] << 16)
+                | (staticarray_backpatch_table[ix+3] << 8)
+                | (staticarray_backpatch_table[ix+4]);
             while (jx<offset) {
                 sf_put(static_array_area[jx]);
                 size++;
@@ -1567,45 +1463,45 @@ extern void write_debug_locations(debug_locations locations)
 }
 
 extern void write_debug_optional_identifier(int32 symbol_index)
-{   if (stypes[symbol_index] != ROUTINE_T)
+{   if (symbols[symbol_index].type != ROUTINE_T)
     {   compiler_error
             ("Attempt to write a replaceable identifier for a non-routine");
     }
-    if (replacement_debug_backpatch_positions[symbol_index].valid)
+    if (symbol_debug_info[symbol_index].replacement_backpatch_pos.valid)
     {   if (fsetpos
                 (Debug_fp,
-                 &replacement_debug_backpatch_positions[symbol_index].position))
+                 &symbol_debug_info[symbol_index].replacement_backpatch_pos.position))
         {   fatalerror("I/O failure: can't seek in debugging information file");
         }
         debug_file_printf
             ("<identifier artificial=\"true\">%s "
                  "(superseded replacement)</identifier>",
-             symbs[symbol_index]);
+             symbols[symbol_index].name);
         if (fseek(Debug_fp, 0L, SEEK_END))
         {   fatalerror("I/O failure: can't seek in debugging information file");
         }
     }
     fgetpos
-      (Debug_fp, &replacement_debug_backpatch_positions[symbol_index].position);
-    replacement_debug_backpatch_positions[symbol_index].valid = TRUE;
-    debug_file_printf("<identifier>%s</identifier>", symbs[symbol_index]);
+      (Debug_fp, &symbol_debug_info[symbol_index].replacement_backpatch_pos.position);
+    symbol_debug_info[symbol_index].replacement_backpatch_pos.valid = TRUE;
+    debug_file_printf("<identifier>%s</identifier>", symbols[symbol_index].name);
     /* Space for:       artificial="true" (superseded replacement) */
     debug_file_printf("                                           ");
 }
 
 extern void write_debug_symbol_backpatch(int32 symbol_index)
-{   if (symbol_debug_backpatch_positions[symbol_index].valid) {
+{   if (symbol_debug_info[symbol_index].backpatch_pos.valid) {
         compiler_error("Symbol entry incorrectly reused in debug information "
                        "file backpatching");
     }
-    fgetpos(Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position);
-    symbol_debug_backpatch_positions[symbol_index].valid = TRUE;
+    fgetpos(Debug_fp, &symbol_debug_info[symbol_index].backpatch_pos.position);
+    symbol_debug_info[symbol_index].backpatch_pos.valid = TRUE;
     /* Reserve space for up to 10 digits plus a negative sign. */
     debug_file_printf("*BACKPATCH*");
 }
 
 extern void write_debug_symbol_optional_backpatch(int32 symbol_index)
-{   if (symbol_debug_backpatch_positions[symbol_index].valid) {
+{   if (symbol_debug_info[symbol_index].backpatch_pos.valid) {
         compiler_error("Symbol entry incorrectly reused in debug information "
                        "file backpatching");
     }
@@ -1614,8 +1510,8 @@ extern void write_debug_symbol_optional_backpatch(int32 symbol_index)
        so that we'll be in the same case as above if the symbol is eventually
        defined. */
     debug_file_printf("<value>");
-    fgetpos(Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position);
-    symbol_debug_backpatch_positions[symbol_index].valid = TRUE;
+    fgetpos(Debug_fp, &symbol_debug_info[symbol_index].backpatch_pos.position);
+    symbol_debug_info[symbol_index].backpatch_pos.valid = TRUE;
     debug_file_printf("*BACKPATCH*</value>");
 }
 
@@ -1731,18 +1627,18 @@ extern void end_writing_debug_sections(int32 end_address)
 }
 
 extern void write_debug_undef(int32 symbol_index)
-{   if (!symbol_debug_backpatch_positions[symbol_index].valid)
+{   if (!symbol_debug_info[symbol_index].backpatch_pos.valid)
     {   compiler_error
             ("Attempt to erase debugging information never written or since "
                 "erased");
     }
-    if (stypes[symbol_index] != CONSTANT_T)
+    if (symbols[symbol_index].type != CONSTANT_T)
     {   compiler_error
             ("Attempt to erase debugging information for a non-constant "
              "because of an #undef");
     }
     if (fsetpos
-         (Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position))
+         (Debug_fp, &symbol_debug_info[symbol_index].backpatch_pos.position))
     {   fatalerror("I/O failure: can't seek in debugging information file");
     }
     /* There are 7 characters in ``<value>''. */
@@ -1752,7 +1648,7 @@ extern void write_debug_undef(int32 symbol_index)
     /* Overwrite:      <value>*BACKPATCH*</value> */
     debug_file_printf("                          ");
     nullify_debug_file_position
-        (&symbol_debug_backpatch_positions[symbol_index]);
+        (&symbol_debug_info[symbol_index].backpatch_pos);
     if (fseek(Debug_fp, 0L, SEEK_END))
     {   fatalerror("I/O failure: can't seek in debugging information file");
     }
@@ -1783,14 +1679,13 @@ static void apply_debug_information_backpatches
 static void apply_debug_information_symbol_backpatches()
 {   int backpatch_symbol;
     for (backpatch_symbol = no_symbols; backpatch_symbol--;)
-    {   if (symbol_debug_backpatch_positions[backpatch_symbol].valid)
+    {   if (symbol_debug_info[backpatch_symbol].backpatch_pos.valid)
         {   if (fsetpos(Debug_fp,
-                        &symbol_debug_backpatch_positions
-                            [backpatch_symbol].position))
+                        &symbol_debug_info[backpatch_symbol].backpatch_pos.position))
             {   fatalerror
                     ("I/O failure: can't seek in debugging information file");
             }
-            debug_file_printf("%11d", svals[backpatch_symbol]);
+            debug_file_printf("%11d", symbols[backpatch_symbol].value);
         }
     }
 }
@@ -1834,57 +1729,6 @@ extern void end_debug_file()
     close_debug_file();
 }
 
-/* ------------------------------------------------------------------------- */
-/*  Temporary storage files:                                                 */
-/*                                                                           */
-/*      Temp file 1 is used to hold the static strings area, as compiled     */
-/*                2 to hold compiled routines of Z-code                      */
-/*                3 to hold the link data table (but only for modules)       */
-/*                                                                           */
-/*  (Though annoying, this procedure typically saves about 200K of memory,   */
-/*  an important point for Amiga and sub-386 PC users of Inform)             */
-/* ------------------------------------------------------------------------- */
-
-extern void open_temporary_files(void)
-{   translate_temp_filename(1);
-    Temp1_fp=fopen(Temp1_Name,"wb");
-    if (Temp1_fp==NULL) fatalerror_named("Couldn't open temporary file 1",
-        Temp1_Name);
-    translate_temp_filename(2);
-    Temp2_fp=fopen(Temp2_Name,"wb");
-    if (Temp2_fp==NULL) fatalerror_named("Couldn't open temporary file 2",
-        Temp2_Name);
-
-    if (!module_switch) return;
-    translate_temp_filename(3);
-    Temp3_fp=fopen(Temp3_Name,"wb");
-    if (Temp3_fp==NULL) fatalerror_named("Couldn't open temporary file 3",
-        Temp3_Name);
-}
-
-extern void check_temp_files(void)
-{
-    if (ferror(Temp1_fp))
-        fatalerror("I/O failure: couldn't write to temporary file 1");
-    if (ferror(Temp2_fp))
-        fatalerror("I/O failure: couldn't write to temporary file 2");
-    if (module_switch && ferror(Temp3_fp))
-        fatalerror("I/O failure: couldn't write to temporary file 3");
-}
-
-extern void remove_temp_files(void)
-{   if (Temp1_fp != NULL) fclose(Temp1_fp);
-    Temp1_fp = NULL;
-    if (Temp2_fp != NULL) fclose(Temp2_fp);
-    Temp2_fp = NULL;
-    remove(Temp1_Name); remove(Temp2_Name);
-    if (module_switch)
-    {   if (Temp3_fp != NULL) fclose(Temp3_fp);
-        Temp3_fp = NULL;
-        remove(Temp3_Name);
-    }
-}
-
 /* ========================================================================= */
 /*   Data structure management routines                                      */
 /* ------------------------------------------------------------------------- */
@@ -1908,8 +1752,6 @@ extern void files_begin_prepass(void)
 
 extern void files_begin_pass(void)
 {   total_chars_read=0;
-    if (temporary_files_switch)
-        open_temporary_files();
 }
 
 static void initialise_accumulator
@@ -1927,10 +1769,9 @@ static void initialise_accumulator
 }
 
 extern void files_allocate_arrays(void)
-{   filename_storage = my_malloc(MAX_SOURCE_FILES*64, "filename storage");
-    filename_storage_p = filename_storage;
-    filename_storage_left = MAX_SOURCE_FILES*64;
-    InputFiles = my_malloc(MAX_SOURCE_FILES*sizeof(FileId), 
+{
+    initialise_memory_list(&InputFiles_memlist,
+        sizeof(FileId), 16, (void**)&InputFiles,
         "input file storage");
     if (debugfile_switch)
     {   if (glulx_mode)
@@ -1959,8 +1800,14 @@ static void tear_down_accumulator(debug_backpatch_accumulator *accumulator)
 }
 
 extern void files_free_arrays(void)
-{   my_free(&filename_storage, "filename storage");
-    my_free(&InputFiles, "input file storage");
+{
+    int ix;
+    for (ix=0; ix<total_files; ix++)
+    {
+        my_free(&InputFiles[ix].filename, "filename storage");
+    }
+    deallocate_memory_list(&InputFiles_memlist);
+    
     if (debugfile_switch)
     {   if (!glulx_mode)
         {   tear_down_accumulator(&object_backpatch_accumulator);
index 97b6d8bca2a831060ce9303122913229bbea7d0f..d92e0f54cc11b1295c0b3563857957260e9e98be 100644 (file)
@@ -1,12 +1,10 @@
 /* ------------------------------------------------------------------------- */
 /*   Header file for Inform:  Z-machine ("Infocom" format) compiler          */
 /*                                                                           */
-/*                              Inform 6.35                                  */
+/*                              Inform 6.40                                  */
 /*                                                                           */
 /*   This header file and the others making up the Inform source code are    */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
-/*                                                                           */
-/* This file is part of Inform.                                              */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -21,9 +19,6 @@
 /* You should have received a copy of the GNU General Public License         */
 /* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
-/*   For detailed documentation on how this program internally works, and    */
-/*   how to port it to a new environment, see the Technical Manual.          */
-/*                                                                           */
 /*   *** To compile this program in one of the existing ports, you must      */
 /*       at least change the machine definition (on the next page).          */
 /*       In most cases no other work will be needed. ***                     */
@@ -39,8 +34,8 @@
 /* ------------------------------------------------------------------------- */
 
 /* For releases, set to the release date in the form "1st January 2000" */
-#define RELEASE_DATE "22nd May 2021"
-#define RELEASE_NUMBER 1635
+#define RELEASE_DATE "in development"
+#define RELEASE_NUMBER 1640
 #define GLULX_RELEASE_NUMBER 38
 #define MODULE_VERSION_NUMBER 1
 #define VNUMBER RELEASE_NUMBER
@@ -70,7 +65,7 @@
 /*     #define VMS         -  for VAX or ALPHA under DEC C, but not VAX C    */
 /*                                                                           */
 /*     In most cases executables are already available at                    */
-/*     http://www.ifarchive.org/, and these are sometimes enhanced with      */
+/*     https://www.ifarchive.org/, and these are sometimes enhanced with     */
 /*     e.g. windowed interfaces whose source is not archived with the        */
 /*     main Inform source.]                                                  */
 /*                                                                           */
@@ -79,8 +74,6 @@
 /*   out a block of definitions like those below.)                           */
 /* ------------------------------------------------------------------------- */
 
-/* #define UNIX */
-
 /* ------------------------------------------------------------------------- */
 /*   The first task is to include the ANSI header files, and typedef         */
 /*   suitable 32-bit integer types.                                          */
 /*   2. Some miscellaneous #define options (set if the constant is           */
 /*   defined, otherwise not set):                                            */
 /*                                                                           */
-/*   USE_TEMPORARY_FILES - use scratch files for workspace, not memory,      */
-/*                         by default                                        */
 /*   PROMPT_INPUT        - prompt input (don't use Unix-style command line)  */
 /*   TIME_UNAVAILABLE    - don't use ANSI time routines to work out today's  */
 /*                         date                                              */
 /*   HAS_REALPATH        - the POSIX realpath() function is available to     */
 /*                         find the absolute path to a file                  */
 /*                                                                           */
-/*   3. An estimate of the typical amount of memory likely to be free        */
-/*   should be given in DEFAULT_MEMORY_SIZE.                                 */
-/*   For most modern machines, HUGE_SIZE is the appropriate setting, but     */
-/*   some older micros may benefit from SMALL_SIZE.                          */
+/*   3. This was DEFAULT_MEMORY_SIZE, now withdrawn.                         */
 /* ------------------------------------------------------------------------- */
 
-#define LARGE_SIZE   1
-#define SMALL_SIZE   2
-#define HUGE_SIZE    3
-
 /* ------------------------------------------------------------------------- */
 /*   4. Filenaming definitions:                                              */
 /*                                                                           */
 /*   is set without STANDARD_DIRECTORIES, as then Inform may                 */
 /*   overwrite its source with object code.                                  */
 /*                                                                           */
-/*   5. Filenames (or code related to filenames) for the three temporary     */
-/*   files.  These only exist during compilation (and only if -F1 is set).   */
-/*   Temporary_Name is the body of a filename to use                         */
-/*   (if you don't set this, it becomes "Inftemp") and Temporary_Directory   */
-/*   is the directory path for the files to go in (which can be altered on   */
-/*   the command line).  On some multi-tasking OSs these filenames ought to  */
-/*   include a number uniquely identifying the process: to indicate this,    */
-/*   define INCLUDE_TASK_ID and provide some code...                         */
-/*                                                                           */
-/*       #define INCLUDE_TASK_ID                                             */
-/*       #ifdef INFORM_FILE                                                  */
-/*       static int32 unique_task_id(void)                                   */
-/*       {   ...some code returning your task ID...                          */
-/*       }                                                                   */
-/*       #endif                                                              */
+/*   5. Filenames (or code related to filenames) for temporary files.        */
+/*   These included Temporary_Name, Temporary_Directory, and                 */
+/*   INCLUDE_TASK_ID. These options have been removed, and are listed here   */
+/*   only for people who might ask "what happened to 5?"                     */
 /*                                                                           */
 /*   6. Any other definitions specific to the OS or machine.                 */
 /*   (In particular DEFAULT_ERROR_FORMAT is 0 on most machines and 1 on PCs; */
 #ifdef AMIGA
 /* 1 */
 #define MACHINE_STRING   "Amiga"
-/* 3 */
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
 /* 4 */
 #define FN_SEP '/'
-/* 5 */
-#define __USE_SYSBASE
-#include <proto/exec.h>
-#define INCLUDE_TASK_ID
-#define Temporary_Directory "T:"
-#ifdef MAIN_INFORM_FILE
-static int32 unique_task_id(void)
-{   return (int32)FindTask(NULL);
-}
-#endif
 #endif
 /* ------------------------------------------------------------------------- */
 /*   ARCHIMEDES block: Acorn/RISC OS settings                                */
@@ -229,17 +190,12 @@ static int32 unique_task_id(void)
 #define MACHINE_STRING   "RISC OS"
 /* 2 */
 #define CHAR_IS_UNSIGNED
-/* 3 */
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
 /* 4 */
 #define FN_SEP '.'
 #define STANDARD_DIRECTORIES
 #define NO_FILE_EXTENSIONS
 #define Source_Directory "inform"
 #define ICL_Directory "ICL"
-/* 5 */
-#define ENABLE_TEMPORARY_PATH
-#define Temporary_Directory "ram:"
 /* 6 */
 #define ARC_THROWBACK
 #endif
@@ -249,20 +205,8 @@ static int32 unique_task_id(void)
 #ifdef ATARIST
 /* 1 */
 #define MACHINE_STRING   "Atari ST"
-/* 3 */
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
 /* 4 */
 #define FN_SEP '/'
-/* 5 */
-#ifndef TOSFS
-#define Temporary_Directory "/tmp"
-#define INCLUDE_TASK_ID
-#ifdef MAIN_INFORM_FILE
-static int32 unique_task_id(void)
-{   return (int32)getpid();
-}
-#endif
-#endif
 #endif
 /* ------------------------------------------------------------------------- */
 /*   BEOS block                                                              */
@@ -270,13 +214,9 @@ static int32 unique_task_id(void)
 #ifdef BEOS
 /* 1 */
 #define MACHINE_STRING   "BeOS"
-/* 3 */
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
 /* 4 */
 #define FN_SEP '/'
 #define FILE_EXTENSIONS
-/* 5 */
-#define Temporary_Directory "/tmp"
 #endif
 /* ------------------------------------------------------------------------- */
 /*   LINUX block                                                             */
@@ -286,14 +226,13 @@ static int32 unique_task_id(void)
 #define MACHINE_STRING   "Linux"
 /* 2 */
 #define HAS_REALPATH
-/* 3 */
-#define DEFAULT_MEMORY_SIZE HUGE_SIZE
 /* 4 */
 #define FN_SEP '/'
-/* 5 */
-#define Temporary_Directory "/tmp"
 /* 6 */
 #define PATHLEN 8192
+#if defined(__STDC__) && (__STDC_VERSION__ >= 201112L)
+#define USE_C11_TIME_API
+#endif
 #endif
 /* ------------------------------------------------------------------------- */
 /*   Macintosh block                                                         */
@@ -318,8 +257,6 @@ static int32 unique_task_id(void)
 #define PROMPT_INPUT
 #endif
 #endif
-/* 3 */
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
 /* 4 */
 #define FN_SEP           ':'
 #ifdef MAC_MPW
@@ -342,8 +279,6 @@ static int32 unique_task_id(void)
 #define MACHINE_STRING   "OS/2"
 /* 2 */
 #define CHAR_IS_UNSIGNED
-/* 3 */
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
 /* 4 */
 #define FN_SEP '/'
 #endif
@@ -355,24 +290,13 @@ static int32 unique_task_id(void)
 #define MACHINE_STRING   "MacOS"
 /* 2 */
 #define HAS_REALPATH
-/* 3 */
-#define DEFAULT_MEMORY_SIZE HUGE_SIZE
 /* 4 */
 #define FN_SEP '/'
-/* 5 */
-#define Temporary_Directory "/tmp"
-#define INCLUDE_TASK_ID
-#define _POSIX_C_SOURCE 199506L
-#define _XOPEN_SOURCE 500
-#ifdef MAIN_INFORM_FILE
-#include <sys/types.h>
-#include <unistd.h>
-static int32 unique_task_id(void)
-{   return (int32)getpid();
-}
-#endif
 /* 6 */
 #define PATHLEN 8192
+#if defined(__STDC__) && (__STDC_VERSION__ >= 201112L)
+#define USE_C11_TIME_API
+#endif
 #endif
 /* ------------------------------------------------------------------------- */
 /*   PC and PC_QUICKC block                                                  */
@@ -384,14 +308,6 @@ static int32 unique_task_id(void)
 #ifdef PC
 /* 1 */
 #define MACHINE_STRING   "PC"
-/* 2 */
-#define USE_TEMPORARY_FILES
-/* 3 */
-#ifdef PC_QUICKC
-#define DEFAULT_MEMORY_SIZE SMALL_SIZE
-#else
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
-#endif
 /* 4 */
 #define FN_SEP '\\'
 /* 6 */
@@ -405,17 +321,13 @@ static int32 unique_task_id(void)
 #define MACHINE_STRING   "Win32"
 /* 2 */
 #define HAS_REALPATH
-/* 3 */
-#define DEFAULT_MEMORY_SIZE HUGE_SIZE
 /* 4 */
 #define FN_SEP '\\'
 /* 6 */
 #define DEFAULT_ERROR_FORMAT 1
 #define PATHLEN 512
-#ifdef _MSC_VER /* Microsoft Visual C++ */
-#define snprintf _snprintf
-#define isnan _isnan
-#define isinf(x) (!_isnan(x) && !_finite(x))
+#if _MSC_VER >= 1920 /* Visual C++ 2019 */
+#define USE_C11_TIME_API
 #endif
 #endif
 /* ------------------------------------------------------------------------- */
@@ -428,20 +340,8 @@ static int32 unique_task_id(void)
 #endif
 /* 2 */
 #define HAS_REALPATH
-/* 3 */
-#define DEFAULT_MEMORY_SIZE HUGE_SIZE
 /* 4 */
 #define FN_SEP '/'
-/* 5 */
-#define PATHLEN 512
-#define Temporary_Directory "/tmp"
-#define INCLUDE_TASK_ID
-#ifdef MAIN_INFORM_FILE
-#include <unistd.h>
-static int32 unique_task_id(void)
-{   return (int32)getpid();
-}
-#endif
 #endif
 /* ------------------------------------------------------------------------- */
 /*   VMS (Dec VAX and Alpha) block                                           */
@@ -459,8 +359,6 @@ static int32 unique_task_id(void)
 #endif
 /* 2 */
 #define CHAR_IS_UNSIGNED
-/* 3 */
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
 /* 4 */
 #define FN_SEP '/'
 #define Code_Extension   ".zip"
@@ -561,9 +459,6 @@ static int32 unique_task_id(void)
 #ifndef Module_Directory
 #define Module_Directory  "modules"
 #endif
-#ifndef Temporary_Directory
-#define Temporary_Directory ""
-#endif
 #ifndef ICL_Directory
 #define ICL_Directory     ""
 #endif
@@ -582,9 +477,6 @@ static int32 unique_task_id(void)
 #ifndef Module_Directory
 #define Module_Directory  ""
 #endif
-#ifndef Temporary_Directory
-#define Temporary_Directory ""
-#endif
 #ifndef ICL_Directory
 #define ICL_Directory     ""
 #endif
@@ -602,18 +494,10 @@ static int32 unique_task_id(void)
 #define PATHLEN 128
 #endif
 
-#ifndef Temporary_File
-#define Temporary_File "Inftemp"
-#endif
-
 #ifndef DEFAULT_ERROR_FORMAT
 #define DEFAULT_ERROR_FORMAT 0
 #endif
 
-#ifndef DEFAULT_MEMORY_SIZE
-#define DEFAULT_MEMORY_SIZE LARGE_SIZE
-#endif
-
 #ifndef CHAR_IS_UNSIGNED
     typedef unsigned char uchar;
 #else
@@ -629,7 +513,9 @@ static int32 unique_task_id(void)
 #endif
 
 /* ------------------------------------------------------------------------- */
-/*   A macro (rather than constant) definition:                              */
+/*   subtract_pointers() measures an address difference in bytes. This is    */
+/*   a macro.                                                                */
+/*   We also declare some memory functions for PC_QUICKC.                    */
 /* ------------------------------------------------------------------------- */
 
 #ifdef PC_QUICKC
@@ -640,6 +526,36 @@ static int32 unique_task_id(void)
 #define subtract_pointers(p1,p2) (((char *) p1)-((char *) p2))
 #endif
 
+
+/* ------------------------------------------------------------------------- */
+/*   Definitions for time measurement. TIMEVALUE is a type; TIMEVALUE_NOW()  */
+/*   sets it; TIMEVALUE_DIFFERENCE() determines a difference in seconds,     */
+/*   as a float.                                                             */
+/*   Modern platforms should support timespec_get() or clock_gettime(). To   */
+/*   use timespec_get(), #define USE_C11_TIME_API. To use clock_gettime(),   */
+/*   #define USE_POSIX_TIME_API. To use the old implementation using         */
+/*   time(), #define USE_OLD_TIME_API. This can only measure in integer      */
+/*   second counts, but it's better than waiting for gnomon.                 */
+/* ------------------------------------------------------------------------- */
+
+#if !defined(USE_C11_TIME_API) && !defined(USE_POSIX_TIME_API) && !defined(USE_OLD_TIME_API)
+#define USE_OLD_TIME_API
+#endif
+
+#if defined(USE_OLD_TIME_API)
+  #define TIMEVALUE time_t
+  #define TIMEVALUE_NOW(t) (*t) = time(0)
+  #define TIMEVALUE_DIFFERENCE(begt, endt) (float)(*(endt) - *(begt))
+#elif defined(USE_C11_TIME_API)
+  #define TIMEVALUE struct timespec
+  #define TIMEVALUE_NOW(t) timespec_get((t), TIME_UTC)
+  #define TIMEVALUE_DIFFERENCE(begt, endt) ((float)((endt)->tv_sec - (begt)->tv_sec) + (float)((endt)->tv_nsec - (begt)->tv_nsec) / 1000000000.0F)
+#elif defined(USE_POSIX_TIME_API)
+  #define TIMEVALUE struct timespec
+  #define TIMEVALUE_NOW(t) clock_gettime(CLOCK_REALTIME, (t))
+  #define TIMEVALUE_DIFFERENCE(begt, endt) ((float)((endt)->tv_sec - (begt)->tv_sec) + (float)((endt)->tv_nsec - (begt)->tv_nsec) / 1000000000.0F)
+#endif
+
 /* ------------------------------------------------------------------------- */
 /*   SEEK_SET is a constant which should be defined in the ANSI header files */
 /*   but which is not present in some implementations: it's used as a        */
@@ -726,42 +642,102 @@ static int32 unique_task_id(void)
 /*   Structure definitions (there are a few others local to files)           */
 /* ------------------------------------------------------------------------- */
 
+/*  A memory list is a sequential array of items. The list grows as
+    necessary, but it is *not* sparse.
+    This can optionally maintain an external pointer (of any type) which 
+    also refers to the allocated array. The external pointer will always
+    have the same value as data.
+    (Note: the external pointer must itself have a stable location, because
+    we keep a pointer *to* it. It cannot live in another memory list or
+    realloced array. Most of our memory lists refer to global or static
+    variables, so that's fine.)
+*/
+typedef struct memory_list_s
+{
+    char *whatfor;   /* must be a static string */
+    void *data;      /* allocated array of count*itemsize bytes */
+    void **extpointer;  /* pointer to keep in sync */
+    size_t itemsize;    /* item size in bytes */
+    size_t count;       /* number of items allocated */
+} memory_list;
+
+typedef struct identstruct_s
+{
+    char text[MAX_IDENTIFIER_LENGTH+1];
+} identstruct;
+
 typedef struct assembly_operand_t
-{   int   type;
+{   int   type;     /* ?_OT value */
     int32 value;
-    int   symtype;   /* 6.30 */
-    int   symflags;  /* 6.30 */
-    int   marker;
+    int   symindex; /* index in symbols array, if derived from a symbol */
+    int   marker;   /* ?_MV value */
 } assembly_operand;
 
-#define INITAOTV(aop, typ, val) ((aop)->type=(typ), (aop)->value=(val), (aop)->marker=0, (aop)->symtype=0, (aop)->symflags=0)
+#define INITAOTV(aop, typ, val) ((aop)->type=(typ), (aop)->value=(val), (aop)->marker=0, (aop)->symindex=-1)
 #define INITAOT(aop, typ) INITAOTV(aop, typ, 0)
 #define INITAO(aop) INITAOTV(aop, 0, 0)
 
-#define  MAX_LINES_PER_VERB 32
+typedef struct variableinfo_s {
+    int32 token;   /* Symbol table index for variable name */
+    int usage;     /* TRUE if referred to */
+} variableinfo;
+
 typedef struct verbt {
     int lines;
-    int l[MAX_LINES_PER_VERB];
+    int *l; /* alloced array */
+    int size; /* allocated size of l */
 } verbt;
 
+typedef struct actioninfo_s {
+    int32 symbol;      /* The symbol table index of the action name */
+    int32 byte_offset; /* The (byte) offset in the Z-machine code area of 
+                          the ...Sub routine */
+} actioninfo;
+
+/* Information about an object class. */
+typedef struct classinfo_s {
+    /* The number of the prototype-object for this class */
+    int object_number;
+    /* The offset of properties block for this class (always an offset inside the properties table) */
+    int32 begins_at;
+    /* Class name symbol number */
+    int32 symbol;
+} classinfo;
+
+/* Common property information. */
+typedef struct commonpropinfo_s {
+    int32 default_value;   /* Common property default value */
+    int is_long;           /* "Long" means "never write a 1-byte value to
+                              this property", and is an obsolete feature:
+                              since Inform 5 all properties have been "long" */
+    int is_additive;       /* "Additive" means that values accumulate rather
+                              than erase each other during class inheritance */
+} commonpropinfo;
+
+/* Property entry record (Z). */
 typedef struct prop {
     uchar l, num;
     assembly_operand ao[32];
 } prop;
 
+/* Properties and attributes of the object currently being constructed (Z). */
 /* Only one of this object. */
 typedef struct fpropt {
     uchar atts[6];
     int l;
     prop pp[64];
+    int32 symbol; /* name symbol or 0 */
 } fpropt;
 
+/* Constructed object (Z). */
 typedef struct objecttz {
     uchar atts[6];
     int parent, next, child;
     int propsize;
+    int32 symbol; /* name symbol or 0 */
 } objecttz;
 
+/* Property entry record (G). */
 typedef struct propg {
     int num;
     int continuation; 
@@ -770,24 +746,38 @@ typedef struct propg {
     int32 datalen;
 } propg;
 
+/* Properties and attributes of the object currently being constructed (G). */
 /* Only one of this object. */
 typedef struct fproptg {
     uchar atts[MAX_NUM_ATTR_BYTES]; 
     int numprops;
-    propg *props;
+    propg *props;               /* allocated to numprops */
+    memory_list props_memlist;
     int propdatasize;
-    assembly_operand *propdata;
+    assembly_operand *propdata; /* allocated to propdatasize */
+    memory_list propdata_memlist;
     int32 finalpropaddr;
+    /* It's safe to use memory_lists in this object because there's just
+       one and it's static. */
+    int32 symbol; /* name symbol or 0 */
 } fproptg;
 
+/* Constructed object (G). */
 typedef struct objecttg {
     /* attributes are stored in a separate array */
     int32 shortname;
     int32 parent, next, child;
     int32 propaddr;
     int32 propsize;
+    int32 symbol; /* name symbol or 0 */
 } objecttg;
 
+typedef struct abbreviation_s {
+    int value;
+    int quality;
+    int freq;
+} abbreviation;
+
 typedef struct maybe_file_position_S
 {   int valid;
     fpos_t position;
@@ -830,23 +820,69 @@ typedef struct debug_location_beginning_s
     int32 orig_beg_char_number;
 } debug_location_beginning;
 
+#define MAX_KEYWORD_GROUP_SIZE (119)
+
 typedef struct keyword_group_s
-{   char *keywords[120];
+{   char *keywords[MAX_KEYWORD_GROUP_SIZE+1]; /* empty-string-terminated */
     int change_token_type;
     int enabled;
     int case_sensitive;
 } keyword_group;
 
-typedef struct token_data_s
-{   char *text;
-    int32 value; /* ###-long */
-    int type;
-    int symtype;  /* 6.30 */
-    int symflags;   /* 6.30 */
-    int marker;
+typedef struct lexeme_data_s {
+    char *text;  /* points at lextexts array */
+    int32 value;
+    int type;    /* a *_TT value */
     debug_location location;
+    int lextext; /* index of text string in lextexts */
+    int context; /* lexical context used to interpret this token */
+} lexeme_data;
+
+typedef struct token_data_s {
+    char *text;
+    int32 value;
+    int type;      /* a *_TT value */
+    int symindex;
+    int symtype;
+    int symflags;
+    int marker;
 } token_data;
 
+typedef struct symbolinfo_s {
+    char *name; /* Points into a symbol_name_space_chunk */
+    int32 value;
+    int marker; /* ?_MV value */
+    brief_location line;
+    unsigned int flags;  /* ?_SFLAGS bitmask */
+    uchar type; /* ?_T value */
+    int next_entry; /* Linked list for symbol hash table */
+} symbolinfo;
+
+typedef struct symboldebuginfo_s {
+    maybe_file_position backpatch_pos;
+    maybe_file_position replacement_backpatch_pos;
+} symboldebuginfo;
+
+typedef struct arrayinfo_s {
+    int32 symbol; /* index in symbols[] */
+    int size;     /* length of array */
+    int type;     /* BYTE_ARRAY, WORD_ARRAY, etc */
+    int loc;      /* true for static, false for dynamic (regular) arrays */
+} arrayinfo;
+
+typedef struct labelinfo_s {
+    int32 offset; /* Offset (zmachine_pc) value */
+    int32 symbol; /* Symbol numbers if defined in source */
+    int next;     /* For linked list */
+    int prev;     /* For linked list */
+} labelinfo;
+
+typedef struct sequencepointinfo_s {
+    int label;               /* Label number */
+    debug_location location; /* Source code reference (used for making
+                                debugging file)                              */
+} sequencepointinfo;
+
 typedef struct FileId_s                 /*  Source code file identifier:     */
 {   char *filename;                     /*  The filename (after translation) */
     FILE *handle;                       /*  Handle of file (when open), or
@@ -855,6 +891,8 @@ typedef struct FileId_s                 /*  Source code file identifier:     */
                                             parsing? If not, this is an
                                             origsource filename (and handle
                                             is NULL).                        */
+    int initial_buffering;              /* Are we still in the initial
+                                           begin_buffering_file() call?      */
 } FileId;
 
 typedef struct ErrorPosition_s
@@ -868,17 +906,6 @@ typedef struct ErrorPosition_s
     int32 orig_char;
 } ErrorPosition;
 
-/*  A memory block can hold at most ALLOC_CHUNK_SIZE * 72:  */
-
-extern int ALLOC_CHUNK_SIZE;
-
-typedef struct memory_block_s
-{   int chunks;
-    int extent_of_last;
-    uchar *chunk[72];
-    int write_pos;
-} memory_block;
-
 /* This serves for both Z-code and Glulx instructions. Glulx doesn't use
    the text, store_variable_number, branch_label_number, or branch_flag
    fields. */
@@ -887,7 +914,7 @@ typedef struct assembly_instruction_t
     int store_variable_number;
     int32 branch_label_number;
     int branch_flag;
-    char *text;
+    char *text;                    /* if set, generally points to token_text */
     int operand_count;
     assembly_operand operand[8];
 } assembly_instruction;
@@ -1318,6 +1345,11 @@ typedef struct operator_s
 #define FAKE_ACTION_T         11
 #define STATIC_ARRAY_T        12
 
+/* These types never occur in the symbol table; they exist only as
+   type-checking requirements. */
+#define STRING_REQ_T          13
+#define DICT_WORD_REQ_T       14
+
 /* ------------------------------------------------------------------------- */
 /*   Statusline_flag values                                                  */
 /* ------------------------------------------------------------------------- */
@@ -1493,6 +1525,7 @@ typedef struct operator_s
 #define WARNING_DK      34
 #define TERMINATING_DK  35
 #define STATIC_DK       36
+#define INDIVIDUAL_DK   37
 
 /*  Index numbers into the keyword group "trace_keywords" (see "lexer.c")  */
 
@@ -1583,8 +1616,8 @@ typedef struct operator_s
 
 #define oddeven_packing_SC            58
 
-#define grammar_table_SC              59     /* Glulx-only */
-#define dictionary_table_SC           60     /* Glulx-only */
+#define grammar_table_SC              59
+#define dictionary_table_SC           60
 #define dynam_string_table_SC         61     /* Glulx-only */
 
 
@@ -1962,6 +1995,19 @@ typedef struct operator_s
 #define STRCTX_SYMBOL    9  /* prop/attr/etc names */
 #define STRCTX_INFIX    10  /* text printed in asterisk traces */
 
+/* ------------------------------------------------------------------------- */
+/*   Bit-flags applying to the execution_never_reaches_here variable.        */
+/*   Note that if any flags are set, UNREACHABLE is set, so we can easily    */
+/*   test "if (execution_never_reaches_here)..."                             */
+/* ------------------------------------------------------------------------- */
+
+#define EXECSTATE_REACHABLE   0  /* compile normally */
+#define EXECSTATE_UNREACHABLE 1  /* execution cannot reach this line */
+#define EXECSTATE_ENTIRE      2  /* execution cannot reach this entire
+                                    statement or code block */
+#define EXECSTATE_NOWARN      4  /* do not print a warning about unreachable
+                                    code */
+
 /* ========================================================================= */
 /*   Initialisation extern definitions                                       */
 /*                                                                           */
@@ -2088,14 +2134,17 @@ extern void verbs_free_arrays(void);
 /*   Extern definitions for "arrays"                                         */
 /* ------------------------------------------------------------------------- */
 
+#define MAX_ZCODE_GLOBAL_VARS (240)
+
 extern int no_globals, no_arrays;
 extern int dynamic_array_area_size;
-extern int *dynamic_array_area;
+extern uchar *dynamic_array_area;
+extern memory_list dynamic_array_area_memlist;
 extern int static_array_area_size;
-extern int *static_array_area;
+extern uchar *static_array_area;
+extern memory_list static_array_area_memlist;
 extern int32 *global_initial_value;
-extern int32 *array_symbols;
-extern int  *array_sizes, *array_types, *array_locs;
+extern arrayinfo *arrays;
 
 extern void make_global(int array_flag, int name_only);
 extern void set_variable_value(int i, int32 v);
@@ -2109,29 +2158,32 @@ extern void finish_array(int32 i, int is_static);
 /*   Extern definitions for "asm"                                            */
 /* ------------------------------------------------------------------------- */
 
-extern memory_block zcode_area;
+extern uchar *zcode_area;
+extern memory_list zcode_area_memlist;
 extern int32 zmachine_pc;
 
 extern int32 no_instructions;
 extern int   sequence_point_follows;
 extern int   uses_unicode_features, uses_memheap_features, 
-    uses_acceleration_features, uses_float_features;
+    uses_acceleration_features, uses_float_features,
+    uses_extundo_features;
 extern debug_location statement_debug_location;
 extern int   execution_never_reaches_here;
-extern int   *variable_usage;
+extern variableinfo *variables;
+extern memory_list variables_memlist;
 extern int   next_label, no_sequence_points;
-extern int32 *variable_tokens;
 extern assembly_instruction AI;
 extern int32 *named_routine_symbols;
 
-extern void print_operand(assembly_operand o);
+extern void print_operand(const assembly_operand *o, int annotate);
 extern char *variable_name(int32 i);
 extern void set_constant_ot(assembly_operand *AO);
 extern int  is_constant_ot(int otval);
 extern int  is_variable_ot(int otval);
-extern void assemblez_instruction(assembly_instruction *a);
-extern void assembleg_instruction(assembly_instruction *a);
+extern void assemblez_instruction(const assembly_instruction *a);
+extern void assembleg_instruction(const assembly_instruction *a);
 extern void assemble_label_no(int n);
+extern int assemble_forward_label_no(int n);
 extern void assemble_jump(int n);
 extern void define_symbol_label(int symbol);
 extern int32 assemble_routine_header(int no_locals, int debug_flag,
@@ -2229,8 +2281,12 @@ extern void parse_assembly(void);
 /*   Extern definitions for "bpatch"                                         */
 /* ------------------------------------------------------------------------- */
 
-extern memory_block zcode_backpatch_table, staticarray_backpatch_table,
-    zmachine_backpatch_table;
+extern uchar *staticarray_backpatch_table;
+extern memory_list staticarray_backpatch_table_memlist;
+extern uchar *zmachine_backpatch_table;
+extern memory_list zmachine_backpatch_table_memlist;
+extern uchar *zcode_backpatch_table;
+extern memory_list zcode_backpatch_table_memlist;
 extern int32 zcode_backpatch_size, staticarray_backpatch_size,
     zmachine_backpatch_size;
 extern int   backpatch_marker, backpatch_error_flag;
@@ -2285,6 +2341,7 @@ extern int  parse_given_directive(int internal_flag);
 /*   Extern definitions for "errors"                                         */
 /* ------------------------------------------------------------------------- */
 
+#define FORERRORS_SIZE (512)
 extern char *forerrors_buff;
 extern int  forerrors_pointer;
 extern int  no_errors, no_warnings, no_suppressed_warnings,
@@ -2295,7 +2352,8 @@ extern ErrorPosition ErrorReport;
 extern void fatalerror(char *s) NORETURN;
 extern void fatalerror_named(char *s1, char *s2) NORETURN;
 extern void memory_out_error(int32 size, int32 howmany, char *name) NORETURN;
-extern void memoryerror(char *s, int32 size) NORETURN;
+extern void error_max_dynamic_strings(int index);
+extern void error_max_abbreviations(int index);
 extern void error(char *s);
 extern void error_named(char *s1, char *s2);
 extern void error_numbered(char *s1, int val);
@@ -2308,6 +2366,7 @@ extern void no_such_label(char *lname);
 extern void warning(char *s);
 extern void warning_numbered(char *s1, int val);
 extern void warning_named(char *s1, char *s2);
+extern void symtype_warning(char *context, char *name, char *type, char *wanttype);
 extern void dbnu_warning(char *type, char *name, brief_location report_line);
 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line);
 extern void obsolete_warning(char *s1);
@@ -2351,6 +2410,7 @@ extern int z_system_constant_list[];
 extern int glulx_system_constant_list[];
 
 extern int32 value_of_system_constant(int t);
+extern char *name_of_system_constant(int t);
 extern void clear_expression_space(void);
 extern void show_tree(assembly_operand AO, int annotate);
 extern assembly_operand parse_expression(int context);
@@ -2365,14 +2425,8 @@ extern int  current_input_file;
 extern int  total_input_files;
 extern FileId *InputFiles;
 
-extern FILE *Temp1_fp, *Temp2_fp, *Temp3_fp;
-extern char Temp1_Name[], Temp2_Name[], Temp3_Name[];
 extern int32 total_chars_read;
 
-extern void open_temporary_files(void);
-extern void check_temp_files(void);
-extern void remove_temp_files(void);
-
 extern void open_transcript_file(char *what_of);
 extern void write_to_transcript_file(char *text, int linetype);
 extern void close_transcript_file(void);
@@ -2432,20 +2486,23 @@ extern int WORDSIZE, INDIV_PROP_START,
     OBJECT_BYTE_LENGTH, DICT_ENTRY_BYTE_LENGTH, DICT_ENTRY_FLAG_POS;
 extern int32 MAXINTWORD;
 
-extern int asm_trace_level, line_trace_level,     expr_trace_level,
+extern int asm_trace_level, expr_trace_level,
     linker_trace_level,     tokens_trace_level;
 
 extern int
-    bothpasses_switch,      concise_switch,
-    economy_switch,         frequencies_switch,
-    ignore_switches_switch, listobjects_switch,   debugfile_switch,
-    listing_switch,         memout_switch,        printprops_switch,
-    offsets_switch,         percentages_switch,   obsolete_switch,
+    concise_switch,
+    economy_switch,         frequencies_setting,
+    ignore_switches_switch, debugfile_switch,
+    files_trace_setting,    memout_switch,        printprops_switch,
+    printactions_switch,
+    obsolete_switch,        optabbrevs_trace_setting,
     transcript_switch,      statistics_switch,    optimise_switch,
     version_set_switch,     nowarnings_switch,    hash_switch,
-    memory_map_switch,      module_switch,        temporary_files_switch,
+    memory_map_setting,     module_switch,
     define_DEBUG_switch,    define_USE_MODULES_switch, define_INFIX_switch,
-    runtime_error_checking_switch;
+    runtime_error_checking_switch,
+    list_verbs_setting,     list_dict_setting,    list_objects_setting,
+    list_symbols_setting;
 
 extern int oddeven_packing_switch;
 
@@ -2453,6 +2510,8 @@ extern int glulx_mode, compression_switch;
 extern int32 requested_glulx_version;
 
 extern int error_format,    store_the_text,       asm_trace_setting,
+    expr_trace_setting,     linker_trace_setting, tokens_trace_setting,
+    bpatch_trace_setting,   symdef_trace_setting,
     double_space_setting,   trace_fns_setting,    character_set_setting,
     character_set_unicode;
 
@@ -2470,7 +2529,6 @@ extern int translate_in_filename(int last_value, char *new_name, char *old_name,
 extern void translate_out_filename(char *new_name, char *old_name);
 extern int translate_link_filename(int last_value,
     char *new_name, char *old_name);
-extern void translate_temp_filename(int i);
 
 #ifdef ARCHIMEDES
 extern char *riscos_file_type(void);
@@ -2491,7 +2549,7 @@ extern int  total_source_line_count;
 extern int  dont_enter_into_symbol_table;
 extern int  return_sp_as_variable;
 extern int  next_token_begins_syntax_line;
-extern char **local_variable_texts;
+extern identstruct *local_variable_names;
 
 extern int32 token_value;
 extern int   token_type;
@@ -2503,7 +2561,9 @@ extern debug_location_beginning get_token_location_beginning(void);
 extern void discard_token_location(debug_location_beginning beginning);
 extern debug_locations get_token_location_end(debug_location_beginning beginning);
 
-extern void describe_token(token_data t);
+extern void describe_token_triple(const char *text, int32 value, int type);
+/* The describe_token() macro works on both token_data and lexeme_data structs. */
+#define describe_token(t) describe_token_triple((t)->text, (t)->value, (t)->type)
 
 extern void construct_local_variable_tables(void);
 extern void declare_systemfile(void);
@@ -2519,6 +2579,7 @@ extern brief_location blank_brief_location;
 
 extern void put_token_back(void);
 extern void get_next_token(void);
+extern void release_token_texts(void);
 extern void restart_lexer(char *lexical_source, char *name);
 
 extern keyword_group directives, statements, segment_markers,
@@ -2530,7 +2591,7 @@ extern keyword_group directives, statements, segment_markers,
 /*   Extern definitions for "linker"                                         */
 /* ------------------------------------------------------------------------- */
 
-extern memory_block link_data_area;
+extern uchar *link_data_area;
 extern int32 link_data_size;
 extern char  current_module_filename[];
 
@@ -2547,27 +2608,21 @@ extern void  link_module(char *filename);
 /*   Extern definitions for "memory"                                         */
 /* ------------------------------------------------------------------------- */
 
-extern int32 malloced_bytes;
+extern size_t malloced_bytes;
 
-extern int MAX_QTEXT_SIZE,  MAX_SYMBOLS,    HASH_TAB_SIZE,   MAX_DICT_ENTRIES,
-           MAX_OBJECTS,     MAX_ACTIONS,    MAX_ADJECTIVES,   MAX_ABBREVS,
-           MAX_STATIC_DATA,      MAX_PROP_TABLE_SIZE,   SYMBOLS_CHUNK_SIZE,
-           MAX_EXPRESSION_NODES, MAX_LABELS,            MAX_LINESPACE,
-           MAX_LOW_STRINGS,      MAX_CLASSES,           MAX_VERBS,
-           MAX_VERBSPACE,        MAX_ARRAYS,            MAX_INCLUSION_DEPTH,
-           MAX_SOURCE_FILES,     MAX_DYNAMIC_STRINGS;
+extern int HASH_TAB_SIZE,
+           MAX_ABBREVS,
+           MAX_DYNAMIC_STRINGS;
 
-extern int32 MAX_STATIC_STRINGS, MAX_ZCODE_SIZE, MAX_LINK_DATA_SIZE,
-           MAX_TRANSCRIPT_SIZE,  MAX_INDIV_PROP_TABLE_SIZE,
-           MAX_NUM_STATIC_STRINGS, MAX_UNICODE_CHARS,
-           MAX_STACK_SIZE, MEMORY_MAP_EXTENSION;
+extern int32 MAX_STACK_SIZE, MEMORY_MAP_EXTENSION;
 
-extern int32 MAX_OBJ_PROP_COUNT, MAX_OBJ_PROP_TABLE_SIZE;
-extern int MAX_LOCAL_VARIABLES, MAX_GLOBAL_VARIABLES;
+extern int MAX_LOCAL_VARIABLES;
 extern int DICT_WORD_SIZE, DICT_CHAR_SIZE, DICT_WORD_BYTES;
 extern int ZCODE_HEADER_EXT_WORDS, ZCODE_HEADER_FLAGS_3;
+extern int ZCODE_LESS_DICT_DATA;
 extern int NUM_ATTR_BYTES, GLULX_OBJECT_EXT_BYTES;
 extern int WARN_UNUSED_ROUTINES, OMIT_UNUSED_ROUTINES;
+extern int STRIP_UNREACHABLE_LABELS;
 extern int TRANSCRIPT_FORMAT;
 
 /* These macros define offsets that depend on the value of NUM_ATTR_BYTES.
@@ -2580,24 +2635,22 @@ extern int TRANSCRIPT_FORMAT;
 #define GOBJFIELD_SIBLING()  (5+((NUM_ATTR_BYTES)/4))
 #define GOBJFIELD_CHILD()    (6+((NUM_ATTR_BYTES)/4))
 
-extern void *my_malloc(int32 size, char *whatfor);
-extern void my_realloc(void *pointer, int32 oldsize, int32 size, 
+extern void *my_malloc(size_t size, char *whatfor);
+extern void my_realloc(void *pointer, size_t oldsize, size_t size, 
     char *whatfor);
-extern void *my_calloc(int32 size, int32 howmany, char *whatfor);
-extern void my_recalloc(void *pointer, int32 size, int32 oldhowmany, 
-    int32 howmany, char *whatfor);
+extern void *my_calloc(size_t size, size_t howmany, char *whatfor);
+extern void my_recalloc(void *pointer, size_t size, size_t oldhowmany, 
+    size_t howmany, char *whatfor);
 extern void my_free(void *pointer, char *whatitwas);
 
-extern void set_memory_sizes(int size_flag);
+extern void set_memory_sizes(void);
 extern void adjust_memory_sizes(void);
 extern void memory_command(char *command);
 extern void print_memory_usage(void);
 
-extern void initialise_memory_block(memory_block *MB);
-extern void deallocate_memory_block(memory_block *MB);
-extern int  read_byte_from_memory_block(memory_block *MB, int32 index);
-extern void write_byte_to_memory_block(memory_block *MB,
-    int32 index, int value);
+extern void initialise_memory_list(memory_list *ML, size_t itemsize, size_t initalloc, void **extpointer, char *whatfor);
+extern void deallocate_memory_list(memory_list *ML);
+extern void ensure_memory_list_available(memory_list *ML, size_t count);
 
 /* ------------------------------------------------------------------------- */
 /*   Extern definitions for "objects"                                        */
@@ -2607,17 +2660,18 @@ extern int no_attributes, no_properties;
 extern int no_individual_properties;
 extern int individuals_length;
 extern uchar *individuals_table;
+extern memory_list individuals_table_memlist;
 extern int no_classes, no_objects;
 extern objecttz *objectsz;
+extern memory_list objectsz_memlist;
 extern objecttg *objectsg;
 extern uchar *objectatts;
-extern int *class_object_numbers;
-extern int32 *class_begins_at;
+extern classinfo *class_info;
+extern memory_list class_info_memlist;
 
-extern int32 *prop_default_value;
-extern int *prop_is_long;
-extern int *prop_is_additive;
-extern char *properties_table;
+extern commonpropinfo *commonprops;
+extern uchar *properties_table;
+extern memory_list properties_table_memlist;
 extern int properties_table_size;
 
 extern void make_attribute(void);
@@ -2636,18 +2690,8 @@ extern void write_the_identifier_names(void);
 
 extern int no_named_constants;
 extern int no_symbols;
-extern int32 **symbs;
-extern int32 *svals;
-extern int   *smarks;
-extern brief_location *slines;
-extern int   *sflags;
-#ifdef VAX
-  extern char *stypes;
-#else
-  extern signed char *stypes;
-#endif
-extern maybe_file_position *symbol_debug_backpatch_positions;
-extern maybe_file_position *replacement_debug_backpatch_positions;
+extern symbolinfo *symbols;
+extern symboldebuginfo *symbol_debug_info;
 extern int32 *individual_name_strings;
 extern int32 *attribute_name_strings;
 extern int32 *action_name_strings;
@@ -2660,13 +2704,17 @@ extern uint32 df_total_size_after_stripping;
 extern char *typename(int type);
 extern int hash_code_from_string(char *p);
 extern int strcmpcis(char *p, char *q);
+extern int get_symbol_index(char *p);
 extern int symbol_index(char *lexeme_text, int hashcode);
 extern void end_symbol_scope(int k);
 extern void describe_symbol(int k);
 extern void list_symbols(int level);
 extern void assign_marked_symbol(int index, int marker, int32 value, int type);
 extern void assign_symbol(int index, int32 value, int type);
+extern void check_warn_symbol_type(const assembly_operand *AO, int wanttype, int wanttype2, char *label);
+extern void check_warn_symbol_has_metaclass(const assembly_operand *AO, char *context);
 extern void issue_unused_warnings(void);
+extern void issue_debug_symbol_warnings(void);
 extern void add_config_symbol_definition(char *symbol, int32 value);
 extern void add_symbol_replacement_mapping(int original, int renamed);
 extern int find_symbol_replacement(int *value);
@@ -2738,24 +2786,26 @@ extern void write_serial_number(char *buffer);
 /*   Extern definitions for "text"                                           */
 /* ------------------------------------------------------------------------- */
 
-extern uchar *low_strings, *low_strings_top;
-extern char  *all_text,    *all_text_top;
+extern uchar *translated_text;
+
+extern uchar *low_strings;
+extern int32 low_strings_top;
 
 extern int   no_abbreviations;
 extern int   abbrevs_lookup_table_made, is_abbreviation;
 extern uchar *abbreviations_at;
-extern int  *abbrev_values;
-extern int  *abbrev_quality;
-extern int  *abbrev_freqs;
+extern abbreviation *abbreviations;
 
 extern int32 total_chars_trans, total_bytes_trans,
              zchars_trans_in_last_string;
 extern int   put_strings_in_low_memory;
 extern int   dict_entries;
-extern uchar *dictionary, *dictionary_top;
+extern uchar *dictionary;
+extern int32 dictionary_top;
 extern int   *final_dict_order;
 
-extern memory_block static_strings_area;
+extern uchar *static_strings_area;
+extern memory_list static_strings_area_memlist;
 extern int32 static_strings_extent;
 
 /* And now, a great many declarations for dealing with Glulx string
@@ -2767,7 +2817,7 @@ extern int no_unicode_chars;
 typedef struct unicode_usage_s unicode_usage_t;
 struct unicode_usage_s {
   int32 ch;
-  unicode_usage_t *next;  
+  int next; /* index in unicode_usage_entries of next */
 };
 
 extern unicode_usage_t *unicode_usage_entries;
@@ -2785,7 +2835,7 @@ typedef struct huffentity_struct {
   int type;
   union {
     int branch[2];
-    unsigned char ch;
+    uchar ch;
     int val;
   } u;
   int depth;
@@ -2806,12 +2856,14 @@ extern void  compress_game_text(void);
 /* end of the Glulx string compression stuff */
 
 extern void  ao_free_arrays(void);
+extern void  extract_all_text(void);
 extern int32 compile_string(char *b, int strctx);
-extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx);
+extern int32 translate_text(int32 p_limit, char *s_text, int strctx);
 extern void  optimise_abbreviations(void);
 extern void  make_abbreviation(char *text);
-extern void  show_dictionary(void);
+extern void  show_dictionary(int level);
 extern void  word_to_ascii(uchar *p, char *result);
+extern void  print_dict_word(int node);
 extern void  write_dictionary_to_transcript(void);
 extern void  sort_dictionary(void);
 extern void  dictionary_prepare(char *dword, uchar *optresult);
@@ -2829,6 +2881,7 @@ extern int32 veneer_routine_address[];
 
 extern void compile_initial_routine(void);
 extern assembly_operand veneer_routine(int code);
+extern char *veneer_routine_name(int code);
 extern void compile_veneer(void);
 
 /* ------------------------------------------------------------------------- */
@@ -2842,8 +2895,9 @@ extern int32 grammar_version_symbol;
 extern verbt *Inform_verbs;
 extern uchar *grammar_lines;
 extern int32 grammar_lines_top;
-extern int32 *action_byte_offset,
-             *grammar_token_routine,
+extern actioninfo *actions;
+extern memory_list actions_memlist;
+extern int32 *grammar_token_routine,
              *adjectives;
 
 extern void find_the_actions(void);
index a72d81ef6e7a62714000768c361e59771763e68b..5c02b6bfe123020f4c05cafc09f55e55993f8b98 100644 (file)
@@ -2,8 +2,8 @@
 /*   "inform" :  The top level of Inform: switches, pathnames, filenaming    */
 /*               conventions, ICL (Inform Command Line) files, main          */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -16,7 +16,7 @@
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -123,6 +123,8 @@ static void select_target(int targ)
     WORDSIZE = 2;
     MAXINTWORD = 0x7FFF;
 
+    MAX_LOCAL_VARIABLES = 16; /* including "sp" */
+
     if (INDIV_PROP_START != 64) {
         INDIV_PROP_START = 64;
         fatalerror("You cannot change INDIV_PROP_START in Z-code");
@@ -139,18 +141,6 @@ static void select_target(int targ)
       NUM_ATTR_BYTES = 6;
       fatalerror("You cannot change NUM_ATTR_BYTES in Z-code");
     }
-    if (MAX_LOCAL_VARIABLES != 16) {
-      MAX_LOCAL_VARIABLES = 16;
-      fatalerror("You cannot change MAX_LOCAL_VARIABLES in Z-code");
-    }
-    if (MAX_GLOBAL_VARIABLES != 240) {
-      MAX_GLOBAL_VARIABLES = 240;
-      fatalerror("You cannot change MAX_GLOBAL_VARIABLES in Z-code");
-    }
-    if (MAX_VERBS > 255) {
-      MAX_VERBS = 255;
-      fatalerror("MAX_VERBS can only go above 255 when Glulx is used");
-    }
   }
   else {
     /* Glulx */
@@ -158,6 +148,10 @@ static void select_target(int targ)
     MAXINTWORD = 0x7FFFFFFF;
     scale_factor = 0; /* It should never even get used in Glulx */
 
+    /* This could really be 120, since the practical limit is the size
+       of local_variables.keywords. But historically it's been 119. */
+    MAX_LOCAL_VARIABLES = 119; /* including "sp" */
+
     if (INDIV_PROP_START < 256) {
         INDIV_PROP_START = 256;
         warning_numbered("INDIV_PROP_START should be at least 256 in Glulx. Setting to", INDIV_PROP_START);
@@ -174,12 +168,11 @@ static void select_target(int targ)
     }
   }
 
-  if (MAX_LOCAL_VARIABLES >= 120) {
-    MAX_LOCAL_VARIABLES = 119;
-    warning("MAX_LOCAL_VARIABLES cannot exceed 119; resetting to 119");
-    /* This is because the keyword table in the lexer only has 120
-       entries. */
+  if (MAX_LOCAL_VARIABLES > MAX_KEYWORD_GROUP_SIZE) {
+    compiler_error("MAX_LOCAL_VARIABLES cannot exceed MAX_KEYWORD_GROUP_SIZE");
+    MAX_LOCAL_VARIABLES = MAX_KEYWORD_GROUP_SIZE;
   }
+
   if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) {
     DICT_WORD_SIZE = MAX_DICT_WORD_SIZE;
     warning_numbered(
@@ -195,21 +188,13 @@ static void select_target(int targ)
     /* MAX_NUM_ATTR_BYTES can be increased in header.h without fear. */
   }
 
-  if (MAX_ADJECTIVES > 255) {
-    MAX_ADJECTIVES = 255;
-    warning("MAX_ADJECTIVES cannot exceed 255; resetting to 255");
-    /* Only used under Grammar__Version 1, which is obsolete. */
-  }
-    
   /* Set up a few more variables that depend on the above values */
 
   if (!targ) {
     /* Z-machine */
     DICT_WORD_BYTES = DICT_WORD_SIZE;
-    /* The Z-code generator doesn't use the following variables, although 
-       it would be a little cleaner if it did. */
     OBJECT_BYTE_LENGTH = 0;
-    DICT_ENTRY_BYTE_LENGTH = (version_number==3)?7:9;
+    DICT_ENTRY_BYTE_LENGTH = ((version_number==3)?7:9) - (ZCODE_LESS_DICT_DATA?1:0);
     DICT_ENTRY_FLAG_POS = 0;
   }
   else {
@@ -245,54 +230,45 @@ static void select_target(int targ)
       MAX_ABBREVS = 64;
     }
   }
-  else {
-    if (MAX_DYNAMIC_STRINGS > 100) {
-      MAX_DYNAMIC_STRINGS = 100;
-      warning("MAX_DYNAMIC_STRINGS cannot exceed 100; resetting to 100");
-      /* This is because they are specified in text literals like "@00",
-         with two digits. */
-    }
-  }
 }
 
 /* ------------------------------------------------------------------------- */
 /*   Tracery: output control variables                                       */
+/*   (These are initially set to foo_trace_setting, but the Trace directive  */
+/*   can change them on the fly)                                             */
 /* ------------------------------------------------------------------------- */
 
 int asm_trace_level,     /* trace assembly: 0 for off, 1 for assembly
-                            only, 2 for full assembly tracing with hex dumps */
-    line_trace_level,    /* line tracing: 0 off, 1 on                        */
-    expr_trace_level,    /* expression tracing: 0 off, 1 full, 2 brief       */
-    linker_trace_level,  /* set by -y: 0 to 4 levels of tracing              */
-    tokens_trace_level;  /* lexer output tracing: 0 off, 1 on                */
+                            only, 2 for full assembly tracing with hex dumps,
+                            3 for branch shortening info, 4 for verbose
+                            branch info                                      */
+    expr_trace_level,    /* expression tracing: 0 off, 1 on, 2/3 more        */
+    linker_trace_level,  /* linker tracing: 0 to 4 levels                    */
+    tokens_trace_level;  /* lexer output tracing: 0 off, 1 on, 2/3 more      */
 
 /* ------------------------------------------------------------------------- */
 /*   On/off switch variables (by default all FALSE); other switch settings   */
+/*   (Some of these have become numerical settings now)                      */
 /* ------------------------------------------------------------------------- */
 
-int bothpasses_switch,              /* -b */
-    concise_switch,                 /* -c */
+int concise_switch,                 /* -c */
     economy_switch,                 /* -e */
-    frequencies_switch,             /* -f */
+    frequencies_setting,            /* $!FREQ, -f */
     ignore_switches_switch,         /* -i */
-    listobjects_switch,             /* -j */
     debugfile_switch,               /* -k */
-    listing_switch,                 /* -l */
-    memout_switch,                  /* -m */
-    printprops_switch,              /* -n */
-    offsets_switch,                 /* -o */
-    percentages_switch,             /* -p */
+    memout_switch,                  /* $!MEM */
+    printprops_switch,              /* $!PROPS */
+    printactions_switch,            /* $!ACTIONS */
     obsolete_switch,                /* -q */
     transcript_switch,              /* -r */
-    statistics_switch,              /* -s */
+    statistics_switch,              /* $!STATS, -s */
     optimise_switch,                /* -u */
     version_set_switch,             /* -v */
     nowarnings_switch,              /* -w */
     hash_switch,                    /* -x */
-    memory_map_switch,              /* -z */
+    memory_map_setting,             /* $!MAP, -z */
     oddeven_packing_switch,         /* -B */
     define_DEBUG_switch,            /* -D */
-    temporary_files_switch,         /* -F */
     module_switch,                  /* -M */
     runtime_error_checking_switch,  /* -S */
     define_USE_MODULES_switch,      /* -U */
@@ -307,53 +283,66 @@ int compression_switch;             /* set by -H */
 int character_set_setting,          /* set by -C0 through -C9 */
     character_set_unicode,          /* set by -Cu */
     error_format,                   /* set by -E */
-    asm_trace_setting,              /* set by -a and -t: value of
-                                       asm_trace_level to use when tracing */
+    asm_trace_setting,              /* $!ASM, -a: initial value of
+                                       asm_trace_level */
+    bpatch_trace_setting,           /* $!BPATCH */
+    symdef_trace_setting,           /* $!SYMDEF */
+    expr_trace_setting,             /* $!EXPR: initial value of
+                                       expr_trace_level */
+    tokens_trace_setting,           /* $!TOKENS: initial value of
+                                       tokens_trace_level */
+    optabbrevs_trace_setting,       /* $!FINDABBREVS */
     double_space_setting,           /* set by -d: 0, 1 or 2 */
-    trace_fns_setting,              /* set by -g: 0, 1 or 2 */
-    linker_trace_setting,           /* set by -y: ditto for linker_... */
+    trace_fns_setting,              /* $!RUNTIME, -g: 0, 1, 2, or 3 */
+    files_trace_setting,            /* $!FILES */
+    linker_trace_setting,           /* $!LINKER: initial value of
+                                       linker_trace_level */
+    list_verbs_setting,             /* $!VERBS */
+    list_dict_setting,              /* $!DICT */
+    list_objects_setting,           /* $!OBJECTS */
+    list_symbols_setting,           /* $!SYMBOLS */
     store_the_text;                 /* when set, record game text to a chunk
-                                       of memory (used by both -r & -k) */
+                                       of memory (used by -u) */
 static int r_e_c_s_set;             /* has -S been explicitly set? */
 
 int glulx_mode;                     /* -G */
 
 static void reset_switch_settings(void)
-{   asm_trace_setting=0;
-    linker_trace_level=0;
-    tokens_trace_level=0;
+{   asm_trace_setting = 0;
+    linker_trace_setting = 0;
+    tokens_trace_setting = 0;
+    expr_trace_setting = 0;
+    bpatch_trace_setting = 0;
+    symdef_trace_setting = 0;
+    list_verbs_setting = 0;
+    list_dict_setting = 0;
+    list_objects_setting = 0;
+    list_symbols_setting = 0;
 
     store_the_text = FALSE;
 
-    bothpasses_switch = FALSE;
     concise_switch = FALSE;
     double_space_setting = 0;
     economy_switch = FALSE;
-    frequencies_switch = FALSE;
+    files_trace_setting = 0;
+    frequencies_setting = 0;
     trace_fns_setting = 0;
     ignore_switches_switch = FALSE;
-    listobjects_switch = FALSE;
     debugfile_switch = FALSE;
-    listing_switch = FALSE;
-    memout_switch = FALSE;
-    printprops_switch = FALSE;
-    offsets_switch = FALSE;
-    percentages_switch = FALSE;
+    memout_switch = 0;
+    printprops_switch = 0;
+    printactions_switch = 0;
     obsolete_switch = FALSE;
     transcript_switch = FALSE;
     statistics_switch = FALSE;
     optimise_switch = FALSE;
+    optabbrevs_trace_setting = 0;
     version_set_switch = FALSE;
     nowarnings_switch = FALSE;
     hash_switch = FALSE;
-    memory_map_switch = FALSE;
+    memory_map_setting = 0;
     oddeven_packing_switch = FALSE;
     define_DEBUG_switch = FALSE;
-#ifdef USE_TEMPORARY_FILES
-    temporary_files_switch = TRUE;
-#else
-    temporary_files_switch = FALSE;
-#endif
     define_USE_MODULES_switch = FALSE;
     module_switch = FALSE;
 #ifdef ARC_THROWBACK
@@ -373,6 +362,12 @@ static void reset_switch_settings(void)
     compression_switch = TRUE;
     glulx_mode = FALSE;
     requested_glulx_version = 0;
+
+    /* These aren't switches, but for clarity we reset them too. */
+    asm_trace_level = 0;
+    expr_trace_level = 0;
+    linker_trace_level = 0;
+    tokens_trace_level = 0;
 }
 
 /* ------------------------------------------------------------------------- */
@@ -428,10 +423,10 @@ static void begin_pass(void)
     files_begin_pass();
 
     endofpass_flag = FALSE;
-    line_trace_level = 0; expr_trace_level = 0;
+    expr_trace_level = expr_trace_setting;
     asm_trace_level = asm_trace_setting;
+    tokens_trace_level = tokens_trace_setting;
     linker_trace_level = linker_trace_setting;
-    if (listing_switch) line_trace_level=1;
 
     lexer_begin_pass();
     linker_begin_pass();
@@ -527,7 +522,6 @@ static char Source_Path[PATHLEN];
 static char Include_Path[PATHLEN];
 static char Code_Path[PATHLEN];
 static char Module_Path[PATHLEN];
-static char Temporary_Path[PATHLEN];
 static char current_source_path[PATHLEN];
        char Debugging_Name[PATHLEN];
        char Transcript_Name[PATHLEN];
@@ -634,7 +628,6 @@ static void set_default_paths(void)
     set_path_value(Code_Path,       Code_Directory);
     set_path_value(Module_Path,     Module_Directory);
     set_path_value(ICL_Path,        ICL_Directory);
-    set_path_value(Temporary_Path,  Temporary_Directory);
     set_path_value(Debugging_Name,  Debugging_File);
     set_path_value(Transcript_Name, Transcript_File);
     set_path_value(Language_Name,   Default_Language);
@@ -676,7 +669,6 @@ static void set_path_command(char *command)
         if (strcmp(pathname, "code_path")==0)    path_to_set=Code_Path;
         if (strcmp(pathname, "module_path")==0)  path_to_set=Module_Path;
         if (strcmp(pathname, "icl_path")==0)     path_to_set=ICL_Path;
-        if (strcmp(pathname, "temporary_path")==0) path_to_set=Temporary_Path;
         if (strcmp(pathname, "debugging_name")==0) path_to_set=Debugging_Name;
         if (strcmp(pathname, "transcript_name")==0) path_to_set=Transcript_Name;
         if (strcmp(pathname, "language_name")==0) path_to_set=Language_Name;
@@ -962,10 +954,8 @@ Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
    name_or_unset(Code_Path));
 
     printf(
-"       Temporary file (out)   temporary_path      %s\n\
-       ICL command file (in)  icl_path            %s\n\
+"       ICL command file (in)  icl_path            %s\n\
        Module (in & out)      module_path         %s\n\n",
-   name_or_unset(Temporary_Path),
    name_or_unset(ICL_Path), name_or_unset(Module_Path));
 
     printf(
@@ -997,7 +987,6 @@ Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
       Include files:   %s\n\
       Story files:     %s (Version 3), %s (v4), %s (v5, the default),\n\
                        %s (v6), %s (v7), %s (v8), %s (Glulx)\n\
-      Temporary files: .tmp\n\
       Modules:         %s\n\n",
       Source_Extension, Include_Extension,
       Code_Extension, V4Code_Extension, V5Code_Extension, V6Code_Extension,
@@ -1072,33 +1061,6 @@ Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
     module_switch = save_mm;
 }
 
-/* ------------------------------------------------------------------------- */
-/*  Naming temporary files                                                   */
-/*       (Arguably temporary files should be made using "tmpfile" in         */
-/*        the ANSI C library, but many supposed ANSI libraries lack it.)     */
-/* ------------------------------------------------------------------------- */
-
-extern void translate_temp_filename(int i)
-{   char *p = NULL;
-    switch(i)
-    {   case 1: p=Temp1_Name; break;
-        case 2: p=Temp2_Name; break;
-        case 3: p=Temp3_Name; break;
-        default: return;
-    }
-    if (strlen(Temporary_Path)+strlen(Temporary_File)+6 >= PATHLEN) {
-        printf ("Temporary_Path is too long.\n");
-        exit(1);
-    }
-    sprintf(p,"%s%s%d", Temporary_Path, Temporary_File, i);
-#ifdef INCLUDE_TASK_ID
-    sprintf(p+strlen(p), "_proc%08lx", (long int) unique_task_id());
-#endif
-#ifdef FILE_EXTENSIONS
-    sprintf(p+strlen(p), ".tmp");
-#endif
-}
-
 #ifdef ARCHIMEDES
 static char riscos_ft_buffer[4];
 
@@ -1137,13 +1099,11 @@ static void run_pass(void)
     lexer_endpass();
     if (module_switch) linker_endpass();
 
+    issue_debug_symbol_warnings();
+    
     close_all_source();
     if (hash_switch && hash_printed_since_newline) printf("\n");
 
-    if (temporary_files_switch)
-    {   if (module_switch) flush_link_data();
-        check_temp_files();
-    }
     sort_dictionary();
     if (track_unused_routines)
         locate_dead_functions();
@@ -1152,9 +1112,8 @@ static void run_pass(void)
 
 int output_has_occurred;
 
-static void rennab(int32 time_taken)
+static void rennab(float time_taken)
 {   /*  rennab = reverse of banner  */
-
     int t = no_warnings + no_suppressed_warnings;
 
     if (memout_switch) print_memory_usage();
@@ -1181,7 +1140,16 @@ static void rennab(int32 time_taken)
     if (no_compiler_errors > 0) print_sorry_message();
 
     if (statistics_switch)
-        printf("Completed in %ld seconds\n", (long int) time_taken);
+    {
+        /* Print the duration to a sensible number of decimal places.
+           (We aim for three significant figures.) */
+        if (time_taken >= 10.0)
+            printf("Completed in %.1f seconds\n", time_taken);
+        else if (time_taken >= 1.0)
+            printf("Completed in %.2f seconds\n", time_taken);
+        else
+            printf("Completed in %.3f seconds\n", time_taken);
+    }
 }
 
 /* ------------------------------------------------------------------------- */
@@ -1191,7 +1159,9 @@ static void rennab(int32 time_taken)
 static int execute_icl_header(char *file1);
 
 static int compile(int number_of_files_specified, char *file1, char *file2)
-{   int32 time_start;
+{
+    TIMEVALUE time_start, time_end;
+    float duration;
 
     if (execute_icl_header(file1))
       return 1;
@@ -1221,7 +1191,9 @@ compiling modules: disabling -S switch\n");
         runtime_error_checking_switch = FALSE;
     }
 
-    time_start=time(0); no_compilations++;
+    TIMEVALUE_NOW(&time_start);
+    
+    no_compilations++;
 
     strcpy(Source_Name, file1); convert_filename_flag = TRUE;
     strcpy(Code_Name, file1);
@@ -1251,15 +1223,22 @@ compiling modules: disabling -S switch\n");
     {   end_debug_file();
     }
 
-    if (temporary_files_switch && (no_errors>0)) remove_temp_files();
+    if (optimise_switch) {
+        /* Pull out all_text so that it will not be freed. */
+        extract_all_text();
+    }
 
     free_arrays();
 
-    rennab((int32) (time(0)-time_start));
-
-    if (optimise_switch) optimise_abbreviations();
+    TIMEVALUE_NOW(&time_end);
+    duration = TIMEVALUE_DIFFERENCE(&time_start, &time_end);
+    
+    rennab(duration);
 
-    if (store_the_text) my_free(&all_text,"transcription text");
+    if (optimise_switch) {
+        optimise_abbreviations();
+        ao_free_arrays();
+    }
 
     return (no_errors==0)?0:1;
 }
@@ -1273,7 +1252,7 @@ static void cli_print_help(int help_level)
     printf(
 "\nThis program is a compiler of Infocom format (also called \"Z-machine\")\n\
 story files, as well as \"Glulx\" story files:\n\
-Copyright (c) Graham Nelson 1993 - 2021.\n\n");
+Copyright (c) Graham Nelson 1993 - 2022.\n\n");
 
    /* For people typing just "inform", a summary only: */
 
@@ -1303,15 +1282,12 @@ One or more words can be supplied as \"commands\". These may be:\n\n\
   
   printf(
 "     $list            list current settings\n\
-     $huge            make standard \"huge game\" settings %s\n\
-     $large           make standard \"large game\" settings %s\n\
-     $small           make standard \"small game\" settings %s\n\
      $?SETTING        explain briefly what SETTING is for\n\
      $SETTING=number  change SETTING to given number\n\
-     $#SYMBOL=number  define SYMBOL as a constant in the story\n\n",
-    (DEFAULT_MEMORY_SIZE==HUGE_SIZE)?"(default)":"",
-    (DEFAULT_MEMORY_SIZE==LARGE_SIZE)?"(default)":"",
-    (DEFAULT_MEMORY_SIZE==SMALL_SIZE)?"(default)":"");
+     $!TRACEOPT       set trace option TRACEOPT\n\
+                      (or $!TRACEOPT=2, 3, etc for more tracing;\n\
+                      $! by itself to list all trace options)\n\
+     $#SYMBOL=number  define SYMBOL as a constant in the story\n\n");
 
   printf(
 "  (filename)    read in a list of commands (in the format above)\n\
@@ -1319,17 +1295,19 @@ One or more words can be supplied as \"commands\". These may be:\n\n\
 
   printf("Alternate command-line formats for the above:\n\
   --help                 (this page)\n\
-  --path PATH=dir\n\
-  --addpath PATH=dir\n\
-  --list\n\
-  --size huge, --size large, --size small\n\
-  --helpopt SETTING\n\
-  --opt SETTING=number\n\
-  --define SETTING=number\n\
-  --config filename      (setup file)\n\n");
+  --path PATH=dir        (set path)\n\
+  --addpath PATH=dir     (add to path)\n\
+  --list                 (list current settings)\n\
+  --helpopt SETTING      (explain setting)\n\
+  --opt SETTING=number   (change setting)\n\
+  --helptrace            (list all trace options)\n\
+  --trace TRACEOPT       (set trace option)\n\
+  --trace TRACEOPT=num   (more tracing)\n\
+  --define SYMBOL=number (define constant)\n\
+  --config filename      (read setup file)\n\n");
 
 #ifndef PROMPT_INPUT
-    printf("For example: \"inform -dexs $huge curses\".\n");
+    printf("For example: \"inform -dexs curses\".\n");
 #endif
 
        return;
@@ -1342,7 +1320,8 @@ One or more words can be supplied as \"commands\". These may be:\n\n\
    /* The -h2 (switches) help information: */
 
    printf("Help on the full list of legal switch commands:\n\n\
-  a   trace assembly-language (without hex dumps; see -t)\n\
+  a   trace assembly-language\n\
+  a2  trace assembly with hex dumps\n\
   c   more concise error messages\n\
   d   contract double spaces after full stops in text\n\
   d2  contract double spaces after exclamation and question marks, too\n\
@@ -1359,19 +1338,12 @@ One or more words can be supplied as \"commands\". These may be:\n\n\
 
    printf("\
   i   ignore default switches set within the file\n\
-  j   list objects as constructed\n\
-  k   output debugging information to \"%s\"\n\
-  l   list every statement run through Inform (not implemented)\n\
-  m   say how much memory has been allocated\n\
-  n   print numbers of properties, attributes and actions\n",
+  k   output debugging information to \"%s\"\n",
           Debugging_Name);
    printf("\
-  o   print offset addresses\n\
-  p   give percentage breakdown of story file\n\
   q   keep quiet about obsolete usages\n\
   r   record all the text to \"%s\"\n\
-  s   give statistics\n\
-  t   trace assembly-language (with full hex dumps; see -a)\n",
+  s   give statistics\n",
       Transcript_Name);
 
    printf("\
@@ -1384,7 +1356,6 @@ One or more words can be supplied as \"commands\". These may be:\n\n\
   v8  compile to version-8 (expanded \"Advanced\") story file\n\
   w   disable warning messages\n\
   x   print # for every 100 lines compiled\n\
-  y   trace linking system\n\
   z   print memory map of the virtual machine\n\n");
 
 printf("\
@@ -1401,11 +1372,6 @@ printf("  E1  Microsoft-style error messages%s\n",
       (error_format==1)?" (current setting)":"");
 printf("  E2  Macintosh MPW-style error messages%s\n",
       (error_format==2)?" (current setting)":"");
-#ifdef USE_TEMPORARY_FILES
-printf("  F0  use extra memory rather than temporary files\n");
-#else
-printf("  F1  use temporary files to reduce memory consumption\n");
-#endif
 printf("  G   compile a Glulx game file\n");
 printf("  H   use Huffman encoding to compress Glulx strings\n");
 printf("  M   compile as a Module for future linking\n");
@@ -1446,8 +1412,14 @@ extern void switches(char *p, int cmode)
         }
         switch(p[i])
         {
-        case 'a': asm_trace_setting = 1; break;
-        case 'b': bothpasses_switch = state; break;
+        case 'a': switch(p[i+1])
+                  {   case '1': asm_trace_setting=1; s=2; break;
+                      case '2': asm_trace_setting=2; s=2; break;
+                      case '3': asm_trace_setting=3; s=2; break;
+                      case '4': asm_trace_setting=4; s=2; break;
+                      default: asm_trace_setting=1; break;
+                  }
+                  break;
         case 'c': concise_switch = state; break;
         case 'd': switch(p[i+1])
                   {   case '1': double_space_setting=1; s=2; break;
@@ -1456,7 +1428,7 @@ extern void switches(char *p, int cmode)
                   }
                   break;
         case 'e': economy_switch = state; break;
-        case 'f': frequencies_switch = state; break;
+        case 'f': frequencies_setting = (state?1:0); break;
         case 'g': switch(p[i+1])
                   {   case '1': trace_fns_setting=1; s=2; break;
                       case '2': trace_fns_setting=2; s=2; break;
@@ -1472,24 +1444,17 @@ extern void switches(char *p, int cmode)
                   }
                   break;
         case 'i': ignore_switches_switch = state; break;
-        case 'j': listobjects_switch = state; break;
         case 'k': if (cmode == 0)
                       error("The switch '-k' can't be set with 'Switches'");
                   else
                       debugfile_switch = state;
                   break;
-        case 'l': listing_switch = state; break;
-        case 'm': memout_switch = state; break;
-        case 'n': printprops_switch = state; break;
-        case 'o': offsets_switch = state; break;
-        case 'p': percentages_switch = state; break;
         case 'q': obsolete_switch = state; break;
         case 'r': if (cmode == 0)
                       error("The switch '-r' can't be set with 'Switches'");
                   else
                       transcript_switch = state; break;
         case 's': statistics_switch = state; break;
-        case 't': asm_trace_setting=2; break;
         case 'u': if (cmode == 0) {
                       error("The switch '-u' can't be set with 'Switches'");
                       break;
@@ -1514,8 +1479,7 @@ extern void switches(char *p, int cmode)
                   break;
         case 'w': nowarnings_switch = state; break;
         case 'x': hash_switch = state; break;
-        case 'y': s=2; linker_trace_setting=p[i+1]-'0'; break;
-        case 'z': memory_map_switch = state; break;
+        case 'z': memory_map_setting = (state ? 1 : 0); break;
         case 'B': oddeven_packing_switch = state; break;
         case 'C': s=2;
                   if (p[i+1] == 'u') {
@@ -1543,16 +1507,6 @@ extern void switches(char *p, int cmode)
                       default:  error_format=1; break;
                   }
                   break;
-        case 'F': if (cmode == 0) {
-                      error("The switch '-F' can't be set with 'Switches'");
-                      break;
-                  }
-                  switch(p[i+1])
-                  {   case '0': s=2; temporary_files_switch = FALSE; break;
-                      case '1': s=2; temporary_files_switch = TRUE; break;
-                      default:  temporary_files_switch = state; break;
-                  }
-                  break;
         case 'M': module_switch = state;
                   if (state && (r_e_c_s_set == FALSE))
                       runtime_error_checking_switch = FALSE;
@@ -1598,22 +1552,15 @@ extern void switches(char *p, int cmode)
         }
     }
 
-    if (optimise_switch && (!store_the_text))
-    {   store_the_text=TRUE;
-#ifdef PC_QUICKC
-        if (memout_switch)
-            printf("Allocation %ld bytes for transcription text\n",
-                (long) MAX_TRANSCRIPT_SIZE);
-        all_text = halloc(MAX_TRANSCRIPT_SIZE,1);
-        malloced_bytes += MAX_TRANSCRIPT_SIZE;
-        if (all_text==NULL)
-         fatalerror("Can't hallocate memory for transcription text.  Darn.");
-#else
-        all_text=my_malloc(MAX_TRANSCRIPT_SIZE,"transcription text");
-#endif
+    if (optimise_switch)
+    {
+        /* store_the_text is equivalent to optimise_switch; -u sets both.
+           We could simplify this. */
+        store_the_text=TRUE;
     }
 }
 
+/* Check whether the string looks like an ICL command. */
 static int icl_command(char *p)
 {   if ((p[0]=='+')||(p[0]=='-')||(p[0]=='$')
         || ((p[0]=='(')&&(p[strlen(p)-1]==')')) ) return TRUE;
@@ -1844,6 +1791,7 @@ static int execute_dashdash_command(char *p, char *p2)
     }
     else if (!strcmp(p, "size")) {
         consumed2 = TRUE;
+        /* We accept these arguments even though they've been withdrawn. */
         if (!(p2 && (!strcmpcis(p2, "HUGE") || !strcmpcis(p2, "LARGE") || !strcmpcis(p2, "SMALL")))) {
             printf("--size must be followed by \"huge\", \"large\", or \"small\"\n");
             return consumed2;
@@ -1902,6 +1850,17 @@ static int execute_dashdash_command(char *p, char *p2)
         }
         snprintf(cli_buff, CMD_BUF_SIZE, "(%s)", p2);
     }
+    else if (!strcmp(p, "trace")) {
+        consumed2 = TRUE;
+        if (!p2) {
+            printf("--trace must be followed by \"traceopt\" or \"traceopt=N\"\n");
+            return consumed2;
+        }
+        snprintf(cli_buff, CMD_BUF_SIZE, "$!%s", p2);
+    }
+    else if (!strcmp(p, "helptrace")) {
+        strcpy(cli_buff, "$!");
+    }
     else {
         printf("Option \"--%s\" unknown (try \"inform -h\")\n", p);
         return FALSE;
@@ -2039,7 +1998,7 @@ static int sub_main(int argc, char **argv)
 
     banner();
 
-    set_memory_sizes(DEFAULT_MEMORY_SIZE); set_default_paths();
+    set_memory_sizes(); set_default_paths();
     reset_switch_settings(); select_version(5);
 
     cli_files_specified = 0; no_compilations = 0;
index f79a6e6118c823aed23552c54b77431786ca41c0..58841268af2ebfd495b5d6f86ea75573a5041081 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "lexer" : Lexical analyser                                              */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -15,7 +15,7 @@
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -45,7 +45,7 @@ int next_token_begins_syntax_line;      /* When TRUE, start a new syntax
 int32 last_mapped_line;  /* Last syntax line reported to debugging file      */
 
 /* ------------------------------------------------------------------------- */
-/*   The lexer's output is a sequence of triples, each called a "token",     */
+/*   The lexer's output is a sequence of structs, each called a "token",     */
 /*   representing one lexical unit (or "lexeme") each.  Instead of providing */
 /*   "lookahead" (that is, always having available the next token after the  */
 /*   current one, so that syntax analysers higher up in Inform can have      */
@@ -59,6 +59,8 @@ int32 last_mapped_line;  /* Last syntax line reported to debugging file      */
 /* ------------------------------------------------------------------------- */
 /*   These three variables are set to the current token on a call to         */
 /*   get_next_token() (but are not changed by a call to put_token_back()).   */
+/*   (It would be tidier to use a token_data structure, rather than having   */
+/*   get_next_token() unpack three values. But this is the way it is.)       */
 /* ------------------------------------------------------------------------- */
 
 int token_type;
@@ -226,6 +228,11 @@ extern debug_locations get_token_location_end
 /*   maximum number of tokens ever put back at once, plus 1 (in effect, the  */
 /*   maximum token lookahead ever needed in syntax analysis, plus 1).        */
 /*                                                                           */
+/*   Note that the circle struct type is lexeme_data, whereas the expression */
+/*   code all works in token_data. They have slightly different needs. The   */
+/*   data is exported through the token_text, token_value, token_type        */
+/*   globals, so there's no need to use the same struct at both levels.      */
+/*                                                                           */
 /*   Unlike some compilers, Inform does not have a context-free lexer: in    */
 /*   fact it has 12288 different possible states.  However, the context only */
 /*   affects the interpretation of "identifiers": lexemes beginning with a   */
@@ -245,24 +252,36 @@ extern debug_locations get_token_location_end
      old-style "objectloop (a in b)" and a new "objectloop (a in b ...)".)   */
 
 static int circle_position;
-static token_data circle[CIRCLE_SIZE];
-
-static int token_contexts[CIRCLE_SIZE];
+static lexeme_data circle[CIRCLE_SIZE];
 
 /* ------------------------------------------------------------------------- */
 /*   A complication, however, is that the text of some lexemes needs to be   */
 /*   held in Inform's memory for much longer periods: for example, a         */
 /*   dictionary word lexeme (like "'south'") must have its text preserved    */
 /*   until the code generation time for the expression it occurs in, when    */
-/*   the dictionary reference is actually made.  Code generation in general  */
-/*   occurs as early as possible in Inform: pending some better method of    */
-/*   garbage collection, we simply use a buffer so large that unless         */
-/*   expressions spread across 10K of source code are found, there can be    */
-/*   no problem.                                                             */
+/*   the dictionary reference is actually made. We handle this by keeping    */
+/*   all lexeme text until the end of the statement (or, for top-level       */
+/*   directives, until the end of the directive). Then we call               */
+/*   release_token_texts() to start over. The lextexts array will therefore  */
+/*   grow to the largest number of lexemes in a single statement or          */
+/*   directive.                                                              */
 /* ------------------------------------------------------------------------- */
 
-static char *lexeme_memory;
-static char *lex_p;                     /* Current write position            */
+typedef struct lextext_s {
+    char *text;
+    size_t size; /* Allocated size (including terminal null)
+                    This is always at least MAX_IDENTIFIER_LENGTH+1         */
+} lextext;
+
+static lextext *lextexts; /* Allocated to no_lextexts */
+static memory_list lextexts_memlist;
+static int no_lextexts;
+
+static int cur_lextexts;    /* Number of lextexts in current use
+                               (cur_lextexts <= no_lextexts)                 */
+
+static int lex_index;       /* Index of lextext being written to             */
+static int lex_pos;         /* Current write position in that lextext        */
 
 /* ------------------------------------------------------------------------- */
 /*   The lexer itself needs up to 3 characters of lookahead (it uses an      */
@@ -294,7 +313,10 @@ static int tokens_put_back;             /* Count of the number of backward
                                            moves made from the last-read
                                            token                             */
 
-extern void describe_token(token_data t)
+/* This gets called for both token_data and lexeme_data structs. It prints
+   a description of the common part (the text, value, type fields). 
+*/
+extern void describe_token_triple(const char *text, int32 value, int type)
 {
     /*  Many of the token types are not set in this file, but later on in
         Inform's higher stages (for example, in the expression evaluator);
@@ -302,51 +324,51 @@ extern void describe_token(token_data t)
 
     printf("{ ");
 
-    switch(t.type)
+    switch(type)
     {
         /*  The following token types occur in lexer output:                 */
 
         case SYMBOL_TT:          printf("symbol ");
-                                 describe_symbol(t.value);
+                                 describe_symbol(value);
                                  break;
-        case NUMBER_TT:          printf("literal number %d", t.value);
+        case NUMBER_TT:          printf("literal number %d", value);
                                  break;
-        case DQ_TT:              printf("string \"%s\"", t.text);
+        case DQ_TT:              printf("string \"%s\"", text);
                                  break;
-        case SQ_TT:              printf("string '%s'", t.text);
+        case SQ_TT:              printf("string '%s'", text);
                                  break;
-        case SEP_TT:             printf("separator '%s'", t.text);
+        case SEP_TT:             printf("separator '%s'", text);
                                  break;
         case EOF_TT:             printf("end of file");
                                  break;
 
-        case STATEMENT_TT:       printf("statement name '%s'", t.text);
+        case STATEMENT_TT:       printf("statement name '%s'", text);
                                  break;
-        case SEGMENT_MARKER_TT:  printf("object segment marker '%s'", t.text);
+        case SEGMENT_MARKER_TT:  printf("object segment marker '%s'", text);
                                  break;
-        case DIRECTIVE_TT:       printf("directive name '%s'", t.text);
+        case DIRECTIVE_TT:       printf("directive name '%s'", text);
                                  break;
-        case CND_TT:             printf("textual conditional '%s'", t.text);
+        case CND_TT:             printf("textual conditional '%s'", text);
                                  break;
-        case OPCODE_NAME_TT:     printf("opcode name '%s'", t.text);
+        case OPCODE_NAME_TT:     printf("opcode name '%s'", text);
                                  break;
-        case SYSFUN_TT:          printf("built-in function name '%s'", t.text);
+        case SYSFUN_TT:          printf("built-in function name '%s'", text);
                                  break;
-        case LOCAL_VARIABLE_TT:  printf("local variable name '%s'", t.text);
+        case LOCAL_VARIABLE_TT:  printf("local variable name '%s'", text);
                                  break;
-        case MISC_KEYWORD_TT:    printf("statement keyword '%s'", t.text);
+        case MISC_KEYWORD_TT:    printf("statement keyword '%s'", text);
                                  break;
-        case DIR_KEYWORD_TT:     printf("directive keyword '%s'", t.text);
+        case DIR_KEYWORD_TT:     printf("directive keyword '%s'", text);
                                  break;
-        case TRACE_KEYWORD_TT:   printf("'trace' keyword '%s'", t.text);
+        case TRACE_KEYWORD_TT:   printf("'trace' keyword '%s'", text);
                                  break;
-        case SYSTEM_CONSTANT_TT: printf("system constant name '%s'", t.text);
+        case SYSTEM_CONSTANT_TT: printf("system constant name '%s'", text);
                                  break;
 
         /*  The remaining are etoken types, not set by the lexer             */
 
         case OP_TT:              printf("operator '%s'",
-                                     operators[t.value].description);
+                                     operators[value].description);
                                  break;
         case ENDEXP_TT:          printf("end of expression");
                                  break;
@@ -354,26 +376,26 @@ extern void describe_token(token_data t)
                                  break;
         case SUBCLOSE_TT:        printf("close bracket");
                                  break;
-        case LARGE_NUMBER_TT:    printf("large number: '%s'=%d",t.text,t.value);
+        case LARGE_NUMBER_TT:    printf("large number: '%s'=%d",text,value);
                                  break;
-        case SMALL_NUMBER_TT:    printf("small number: '%s'=%d",t.text,t.value);
+        case SMALL_NUMBER_TT:    printf("small number: '%s'=%d",text,value);
                                  break;
-        case VARIABLE_TT:        printf("variable '%s'=%d", t.text, t.value);
+        case VARIABLE_TT:        printf("variable '%s'=%d", text, value);
                                  break;
-        case DICTWORD_TT:        printf("dictionary word '%s'", t.text);
+        case DICTWORD_TT:        printf("dictionary word '%s'", text);
                                  break;
-        case ACTION_TT:          printf("action name '%s'", t.text);
+        case ACTION_TT:          printf("action name '%s'", text);
                                  break;
 
         default:
             printf("** unknown token type %d, text='%s', value=%d **",
-            t.type, t.text, t.value);
+            type, text, value);
     }
     printf(" }");
 }
 
 /* ------------------------------------------------------------------------- */
-/*   All but one of the 280 Inform keywords (118 of them opcode names used   */
+/*   All but one of the Inform keywords (most of them opcode names used      */
 /*   only by the assembler).  (The one left over is "sp", a keyword used in  */
 /*   assembly language only.)                                                */
 /*                                                                           */
@@ -385,7 +407,9 @@ extern void describe_token(token_data t)
 /*   "header.h" but is otherwise not significant.                            */
 /* ------------------------------------------------------------------------- */
 
-#define MAX_KEYWORDS 350
+/* This must exceed the total number of keywords across all groups, 
+   including opcodes. */
+#define MAX_KEYWORDS (350)
 
 /* The values will be filled in at compile time, when we know
    which opcode set to use. */
@@ -444,6 +468,7 @@ static char *opcode_list_g[] = {
     "sqrt", "exp", "log", "pow",
     "sin", "cos", "tan", "asin", "acos", "atan", "atan2",
     "jfeq", "jfne", "jflt", "jfle", "jfgt", "jfge", "jisnan", "jisinf",
+    "hasundo", "discardundo",
     ""
 };
 
@@ -492,7 +517,7 @@ keyword_group directive_keywords =
     "string", "table", "buffer", "data", "initial", "initstr",
     "with", "private", "has", "class",
     "error", "fatalerror", "warning",
-    "terminating", "static",
+    "terminating", "static", "individual",
     "" },
     DIR_KEYWORD_TT, FALSE, TRUE
 };
@@ -561,8 +586,10 @@ keyword_group *keyword_groups[12]
     &directive_keywords, &misc_keywords, &statements, &conditions,
     &system_functions, &system_constants, &opcode_macros};
 
+/* These keywords are set to point to local_variable_names entries when
+   a routine header is parsed. See construct_local_variable_tables().        */
 keyword_group local_variables =
-{ { "" },                                 /* Filled in when routine declared */
+{ { "" },
     LOCAL_VARIABLE_TT, FALSE, FALSE
 };
 
@@ -618,8 +645,21 @@ static int *keywords_data_table;
 
 static int *local_variable_hash_table;
 static int *local_variable_hash_codes;
-char **local_variable_texts;
-static char *local_variable_text_table;
+
+/* Note that MAX_LOCAL_VARIABLES is the maximum number of local variables
+   for this VM, *including* "sp" (the stack pointer "local").
+   This used to be a memory setting. Now it is a constant: 16 for Z-code,
+   119 for Glulx.
+*/
+
+/* Names of local variables in the current routine.
+   This is allocated to MAX_LOCAL_VARIABLES-1. (The stack pointer "local"
+   is not included in this array.)
+
+   (This could be a memlist, growing as needed up to MAX_LOCAL_VARIABLES-1.
+   But right now we just allocate the max.)
+ */
+identstruct *local_variable_names;
 
 static char one_letter_locals[128];
 
@@ -637,11 +677,21 @@ static void make_keywords_tables(void)
     }
 
     for (j=0; *(oplist[j]); j++) {
+        if (j >= MAX_KEYWORD_GROUP_SIZE) {
+            /* Gotta increase MAX_KEYWORD_GROUP_SIZE */
+            compiler_error("opcode_list has overflowed opcode_names.keywords");
+            break;
+        }
         opcode_names.keywords[j] = oplist[j];
     }
     opcode_names.keywords[j] = "";
     
     for (j=0; *(maclist[j]); j++) {
+        if (j >= MAX_KEYWORD_GROUP_SIZE) {
+            /* Gotta increase MAX_KEYWORD_GROUP_SIZE */
+            compiler_error("opmacro_list has overflowed opcode_macros.keywords");
+            break;
+        }
         opcode_macros.keywords[j] = maclist[j];
     }
     opcode_macros.keywords[j] = "";
@@ -654,7 +704,13 @@ static void make_keywords_tables(void)
     for (i=1; i<=11; i++)
     {   keyword_group *kg = keyword_groups[i];
         for (j=0; *(kg->keywords[j]) != 0; j++)
-        {   h = hash_code_from_string(kg->keywords[j]);
+        {
+            if (tp >= MAX_KEYWORDS) {
+                /* Gotta increase MAX_KEYWORDS */
+                compiler_error("keywords_data_table has overflowed MAX_KEYWORDS");
+                break;
+            }
+            h = hash_code_from_string(kg->keywords[j]);
             if (keywords_hash_table[h] == -1)
                 keywords_hash_table[h] = tp;
             else
@@ -668,32 +724,37 @@ static void make_keywords_tables(void)
     }
 }
 
+/* Look at the strings stored in local_variable_names (from 0 to no_locals).
+   Set local_variables.keywords to point to these, and also prepare the
+   hash tables. */
 extern void construct_local_variable_tables(void)
-{   int i, h; char *p = local_variable_text_table;
+{   int i, h;
     for (i=0; i<HASH_TAB_SIZE; i++) local_variable_hash_table[i] = -1;
     for (i=0; i<128; i++) one_letter_locals[i] = MAX_LOCAL_VARIABLES;
 
     for (i=0; i<no_locals; i++)
-    {   char *q = local_variables.keywords[i];
-        if (q[1] == 0)
-        {   one_letter_locals[(uchar)q[0]] = i;
-            if (isupper(q[0])) one_letter_locals[tolower(q[0])] = i;
-            if (islower(q[0])) one_letter_locals[toupper(q[0])] = i;
+    {
+        char *p = local_variable_names[i].text;
+        local_variables.keywords[i] = p;
+        if (p[1] == 0)
+        {   one_letter_locals[(uchar)p[0]] = i;
+            if (isupper(p[0])) one_letter_locals[tolower(p[0])] = i;
+            if (islower(p[0])) one_letter_locals[toupper(p[0])] = i;
         }
-        h = hash_code_from_string(q);
+        h = hash_code_from_string(p);
         if (local_variable_hash_table[h] == -1)
             local_variable_hash_table[h] = i;
         local_variable_hash_codes[i] = h;
-        local_variable_texts[i] = p;
-        strcpy(p, q);
-        p += strlen(p)+1;
     }
-    for (;i<MAX_LOCAL_VARIABLES-1;i++) 
-      local_variable_texts[i] = "<no such local variable>";
+    /* Clear the rest. */
+    for (;i<MAX_LOCAL_VARIABLES-1;i++) {
+        local_variables.keywords[i] = "";
+        local_variable_hash_codes[i] = 0;
+    }
 }
 
-static void interpret_identifier(int pos, int dirs_only_flag)
-{   int index, hashcode; char *p = circle[pos].text;
+static void interpret_identifier(char *p, int pos, int dirs_only_flag)
+{   int index, hashcode;
 
     /*  An identifier is either a keyword or a "symbol", a name which the
         lexical analyser leaves to higher levels of Inform to understand.    */
@@ -724,7 +785,7 @@ static void interpret_identifier(int pos, int dirs_only_flag)
         if (index >= 0)
         {   for (;index<no_locals;index++)
             {   if (hashcode == local_variable_hash_codes[index])
-                {   if (strcmpcis(p, local_variable_texts[index])==0)
+                {   if (strcmpcis(p, local_variable_names[index].text)==0)
                     {   circle[pos].type = LOCAL_VARIABLE_TT;
                         circle[pos].value = index+1;
                         return;
@@ -1089,10 +1150,6 @@ static void reached_new_line(void)
         if (g_proc != true)
         {   free_arrays();
             close_all_source();
-            if (temporary_files_switch)
-                remove_temp_files();
-            if (store_the_text)
-                my_free(&all_text,"transcription text");
             abort_transcript_file();
             longjmp (g_fallback, 1);
         }
@@ -1143,8 +1200,7 @@ static double pow10_cheap(int expo)
 }
 
 /* Return the IEEE-754 single-precision encoding of a floating-point
- * number. See http://www.psc.edu/general/software/packages/ieee/ieee.php
- * for an explanation.
+ * number.
  *
  * The number is provided in the pieces it was parsed in:
  *    [+|-] intv "." fracv "e" [+|-]expo
@@ -1256,18 +1312,31 @@ typedef struct Sourcefile_s
     LexicalBlock LB;
 } Sourcefile;
 
-static Sourcefile *FileStack;
-static int File_sp;                              /*  Stack pointer           */
+static Sourcefile *FileStack;     /*  Allocated to FileStack_max */
+static memory_list FileStack_memlist;
+static int FileStack_max;         /*  The highest value that File_sp has
+                                      reached
+                                      (Filestack entries to this depth have
+                                      a buffer allocated)                    */
 
-static Sourcefile *CF;                           /*  Top entry on stack      */
+static int File_sp;               /*  Current stack pointer                  */
+static Sourcefile *CF;            /*  Top entry on stack (always equal to
+                                      FileStack[File_sp-1])                  */
 
 static int last_input_file;
 
+/* Set CF and CurrentLB.
+   This does not increment File_sp; the caller must do that. */
 static void begin_buffering_file(int i, int file_no)
 {   int j, cnt; uchar *p;
 
-    if (i >= MAX_INCLUSION_DEPTH) 
-       memoryerror("MAX_INCLUSION_DEPTH",MAX_INCLUSION_DEPTH);
+    CF = NULL;
+    CurrentLB = NULL;
+    
+    ensure_memory_list_available(&FileStack_memlist, i+1);
+    while (i >= FileStack_max) {
+        FileStack[FileStack_max++].buffer = my_malloc(SOURCE_BUFFER_SIZE+4, "source file buffer");
+    }
 
     p = (uchar *) FileStack[i].buffer;
 
@@ -1280,6 +1349,8 @@ static void begin_buffering_file(int i, int file_no)
     FileStack[i].file_no = file_no;
     FileStack[i].size = file_load_chars(file_no,
         (char *) p, SOURCE_BUFFER_SIZE);
+    /* If the file is shorter than SOURCE_BUFFER_SIZE, it's now closed already. We still need to set up the file entry though. */
+    
     lookahead  = source_to_iso_grid[p[0]];
     lookahead2 = source_to_iso_grid[p[1]];
     lookahead3 = source_to_iso_grid[p[2]];
@@ -1299,6 +1370,8 @@ static void begin_buffering_file(int i, int file_no)
     FileStack[i].LB.orig_source = NULL; FileStack[i].LB.orig_file = 0; 
     FileStack[i].LB.orig_line = 0; FileStack[i].LB.orig_char = 0;
 
+    InputFiles[file_no-1].initial_buffering = FALSE;
+    
     CurrentLB = &(FileStack[i].LB);
     CF = &(FileStack[i]);
 
@@ -1376,7 +1449,7 @@ static int get_next_char_from_pipeline(void)
     lookahead3 = source_to_iso_grid[p[CF->read_pos++]];
 
     CurrentLB->chars_read++;
-    if (forerrors_pointer < 511)
+    if (forerrors_pointer < FORERRORS_SIZE-1)
         forerrors_buff[forerrors_pointer++] = current;
     if (current == '\n') reached_new_line();
     return(current);
@@ -1400,7 +1473,7 @@ static int get_next_char_from_string(void)
                     else lookahead3 = source_to_iso_grid[p[3]];
 
     CurrentLB->chars_read++;
-    if (forerrors_pointer < 511)
+    if (forerrors_pointer < FORERRORS_SIZE-1)
         forerrors_buff[forerrors_pointer++] = current;
     if (current == '\n') reached_new_line();
     return(current);
@@ -1418,11 +1491,54 @@ static int get_next_char_from_string(void)
 /*                                       and move the read position forward  */
 /*                                       by one                              */
 /*                                                                           */
+/*       release_token_texts()       discard all the tokens that have been   */
+/*                                       read in, except for put-back ones   */
+/*                                                                           */
 /*       restart_lexer(source, name) if source is NULL, initialise the lexer */
 /*                                       to read from source files;          */
 /*                                   otherwise, to read from this string.    */
 /* ------------------------------------------------------------------------- */
 
+extern void release_token_texts(void)
+{
+    /* This is called at the beginning of every (top-level) directive and
+       every statement. It drops all token usage so that the lextexts
+       array can be reused.
+
+       Call this immediately before a get_next_token() call.
+
+       This should *not* be called within parse_expression(). Expression
+       code generation relies on token data being retained across the whole
+       expression.
+    */
+    int ix;
+
+    token_text = NULL;
+    
+    if (tokens_put_back == 0) {
+        cur_lextexts = 0;
+        return;
+    }
+
+    /* If any tokens have been put back, we have to preserve their text.
+       Move their lextext usage to the head of the lextexts array. */
+    
+    for (ix=0; ix<tokens_put_back; ix++) {
+        int oldpos;
+        lextext temp;
+        int pos = circle_position - tokens_put_back + 1 + ix;
+        if (pos < 0) pos += CIRCLE_SIZE;
+
+        oldpos = circle[pos].lextext;
+        circle[pos].lextext = ix;
+        /* Swap the entire lextext structure (including size) */
+        temp = lextexts[ix];
+        lextexts[ix] = lextexts[oldpos];
+        lextexts[oldpos] = temp;
+    }
+    cur_lextexts = tokens_put_back;
+}
+
 extern void put_token_back(void)
 {   tokens_put_back++;
 
@@ -1441,20 +1557,68 @@ assumption inside Inform");
     }
 }
 
+/* The get_next_token() code reads characters into the current lextext,
+   which is lextexts[lex_index]. It uses these routines to add and remove
+   characters, reallocing when necessary.
+
+   lex_pos is the current number of characters in the lextext. It is
+   not necessarily null-terminated until get_next_token() completes.
+ */
+
+/* Add one character */
+static void lexaddc(char ch)
+{
+    if ((size_t)lex_pos >= lextexts[lex_index].size) {
+        size_t newsize = lextexts[lex_index].size * 2;
+        my_realloc(&lextexts[lex_index].text, lextexts[lex_index].size, newsize, "one lexeme text");
+        lextexts[lex_index].size = newsize;
+    }
+    lextexts[lex_index].text[lex_pos++] = ch;
+}
+
+/* Remove the last character and ensure it's null-terminated */
+static void lexdelc(void)
+{
+    if (lex_pos > 0) {
+        lex_pos--;
+    }
+    lextexts[lex_index].text[lex_pos] = 0;
+}
+
+/* Return the last character */
+static char lexlastc(void)
+{
+    if (lex_pos == 0) {
+        return 0;
+    }
+    return lextexts[lex_index].text[lex_pos-1];
+}
+
+/* Add a string of characters (including the null) */
+static void lexadds(char *str)
+{
+    while (*str) {
+        lexaddc(*str);
+        str++;
+    }
+    lexaddc(0);
+}
+
 extern void get_next_token(void)
 {   int d, i, j, k, quoted_size, e, radix, context; int32 n; char *r;
     int returning_a_put_back_token = TRUE;
-
+    
     context = lexical_context();
 
     if (tokens_put_back > 0)
     {   i = circle_position - tokens_put_back + 1;
         if (i<0) i += CIRCLE_SIZE;
         tokens_put_back--;
-        if (context != token_contexts[i])
+        if (context != circle[i].context)
         {   j = circle[i].type;
             if ((j==0) || ((j>=100) && (j<200)))
-                interpret_identifier(i, FALSE);
+                interpret_identifier(circle[i].text, i, FALSE);
+            circle[i].context = context;
         }
         goto ReturnBack;
     }
@@ -1463,12 +1627,22 @@ extern void get_next_token(void)
     if (circle_position == CIRCLE_SIZE-1) circle_position = 0;
     else circle_position++;
 
-    if (lex_p > lexeme_memory + 4*MAX_QTEXT_SIZE)
-        lex_p = lexeme_memory;
-
-    circle[circle_position].text = lex_p;
+    lex_index = cur_lextexts++;
+    if (lex_index >= no_lextexts) {
+        /* fresh lextext block; must init it */
+        no_lextexts = lex_index+1;
+        ensure_memory_list_available(&lextexts_memlist, no_lextexts);
+        lextexts[lex_index].size = MAX_IDENTIFIER_LENGTH + 1;
+        lextexts[lex_index].text = my_malloc(lextexts[lex_index].size, "one lexeme text");
+    }
+    lex_pos = 0;
+    lextexts[lex_index].text[0] = 0; /* start with an empty string */
+    
+    circle[circle_position].lextext = lex_index;
+    circle[circle_position].text = NULL; /* will fill in later */
     circle[circle_position].value = 0;
-    *lex_p = 0;
+    circle[circle_position].type = 0;
+    circle[circle_position].context = context;
 
     StartTokenAgain:
     d = (*get_next_char)();
@@ -1499,8 +1673,7 @@ extern void get_next_token(void)
 
         case EOF_CODE:
             circle[circle_position].type = EOF_TT;
-            strcpy(lex_p, "<end of file>");
-            lex_p += strlen(lex_p) + 1;
+            lexadds("<end of file>");
             break;
 
         case DIGIT_CODE:
@@ -1509,11 +1682,11 @@ extern void get_next_token(void)
             n=0;
             do
             {   n = n*radix + character_digit_value[d];
-                *lex_p++ = d;
+                lexaddc(d);
             } while ((character_digit_value[lookahead] < radix)
                      && (d = (*get_next_char)(), TRUE));
 
-            *lex_p++ = 0;
+            lexaddc(0);
             circle[circle_position].type = NUMBER_TT;
             circle[circle_position].value = n;
             break;
@@ -1522,38 +1695,38 @@ extern void get_next_token(void)
             {   int expo=0; double intv=0, fracv=0;
                 int expocount=0, intcount=0, fraccount=0;
                 int signbit = (d == '-');
-                *lex_p++ = d;
+                lexaddc(d);
                 while (character_digit_value[lookahead] < 10) {
                     intv = 10.0*intv + character_digit_value[lookahead];
                     intcount++;
-                    *lex_p++ = lookahead;
+                    lexaddc(lookahead);
                     (*get_next_char)();
                 }
                 if (lookahead == '.') {
                     double fracpow = 1.0;
-                    *lex_p++ = lookahead;
+                    lexaddc(lookahead);
                     (*get_next_char)();
                     while (character_digit_value[lookahead] < 10) {
                         fracpow *= 0.1;
                         fracv = fracv + fracpow*character_digit_value[lookahead];
                         fraccount++;
-                        *lex_p++ = lookahead;
+                        lexaddc(lookahead);
                         (*get_next_char)();
                     }
                 }
                 if (lookahead == 'e' || lookahead == 'E') {
                     int exposign = 0;
-                    *lex_p++ = lookahead;
+                    lexaddc(lookahead);
                     (*get_next_char)();
                     if (lookahead == '+' || lookahead == '-') {
                         exposign = (lookahead == '-');
-                        *lex_p++ = lookahead;
+                        lexaddc(lookahead);
                         (*get_next_char)();
                     }
                     while (character_digit_value[lookahead] < 10) {
                         expo = 10*expo + character_digit_value[lookahead];
                         expocount++;
-                        *lex_p++ = lookahead;
+                        lexaddc(lookahead);
                         (*get_next_char)();
                     }
                     if (expocount == 0)
@@ -1564,7 +1737,7 @@ extern void get_next_token(void)
                     error("Floating-point literal must have digits");
                 n = construct_float(signbit, intv, fracv, expo);
             }
-            *lex_p++ = 0;
+            lexaddc(0);
             circle[circle_position].type = NUMBER_TT;
             circle[circle_position].value = n;
             if (!glulx_mode && dont_enter_into_symbol_table != -2) error("Floating-point literals are not available in Z-code");
@@ -1585,15 +1758,15 @@ extern void get_next_token(void)
         case QUOTE_CODE:     /* Single-quotes: scan a literal string */
             quoted_size=0;
             do
-            {   e = d; d = (*get_next_char)(); *lex_p++ = d;
+            {   e = d; d = (*get_next_char)(); lexaddc(d);
                 if (quoted_size++==64)
                 {   error(
                     "Too much text for one pair of quotations '...' to hold");
-                    *lex_p='\''; break;
+                    lexaddc('\''); break;
                 }
                 if ((d == '\'') && (e != '@'))
                 {   if (quoted_size == 1)
-                    {   d = (*get_next_char)(); *lex_p++ = d;
+                    {   d = (*get_next_char)(); lexaddc(d);
                         if (d != '\'')
                             error("No text between quotation marks ''");
                     }
@@ -1601,29 +1774,25 @@ extern void get_next_token(void)
                 }
             } while (d != EOF);
             if (d==EOF) ebf_error("'\''", "end of file");
-            *(lex_p-1) = 0;
+            lexdelc();
             circle[circle_position].type = SQ_TT;
             break;
 
         case DQUOTE_CODE:    /* Double-quotes: scan a literal string */
             quoted_size=0;
             do
-            {   d = (*get_next_char)(); *lex_p++ = d;
-                if (quoted_size++==MAX_QTEXT_SIZE)
-                {   memoryerror("MAX_QTEXT_SIZE", MAX_QTEXT_SIZE);
-                    break;
-                }
+            {   d = (*get_next_char)(); lexaddc(d);
                 if (d == '\n')
-                {   lex_p--;
-                    while (*(lex_p-1) == ' ') lex_p--;
-                    if (*(lex_p-1) != '^') *lex_p++ = ' ';
+                {   lex_pos--;
+                    while (lexlastc() == ' ') lex_pos--;
+                    if (lexlastc() != '^') lexaddc(' ');
                     while ((lookahead != EOF) &&
                           (tokeniser_grid[lookahead] == WHITESPACE_CODE))
                     (*get_next_char)();
                 }
                 else if (d == '\\')
                 {   int newline_passed = FALSE;
-                    lex_p--;
+                    lex_pos--;
                     while ((lookahead != EOF) &&
                           (tokeniser_grid[lookahead] == WHITESPACE_CODE))
                         if ((d = (*get_next_char)()) == '\n')
@@ -1638,40 +1807,44 @@ extern void get_next_token(void)
                 }
             }   while ((d != EOF) && (d!='\"'));
             if (d==EOF) ebf_error("'\"'", "end of file");
-            *(lex_p-1) = 0;
+            lexdelc();
             circle[circle_position].type = DQ_TT;
             break;
 
         case IDENTIFIER_CODE:    /* Letter or underscore: an identifier */
 
-            *lex_p++ = d; n=1;
+            lexaddc(d); n=1;
             while ((n<=MAX_IDENTIFIER_LENGTH)
                    && ((tokeniser_grid[lookahead] == IDENTIFIER_CODE)
                    || (tokeniser_grid[lookahead] == DIGIT_CODE)))
-                n++, *lex_p++ = (*get_next_char)();
+                n++, lexaddc((*get_next_char)());
 
-            *lex_p++ = 0;
+            lexaddc(0);
 
             if (n > MAX_IDENTIFIER_LENGTH)
             {   char bad_length[100];
                 sprintf(bad_length,
                     "Name exceeds the maximum length of %d characters:",
                          MAX_IDENTIFIER_LENGTH);
-                error_named(bad_length, circle[circle_position].text);
+                error_named(bad_length, lextexts[lex_index].text);
+                /* Eat any further extra characters in the identifier */
+                while (((tokeniser_grid[lookahead] == IDENTIFIER_CODE)
+                        || (tokeniser_grid[lookahead] == DIGIT_CODE)))
+                    (*get_next_char)();
                 /* Trim token so that it doesn't violate
                    MAX_IDENTIFIER_LENGTH during error recovery */
-                circle[circle_position].text[MAX_IDENTIFIER_LENGTH] = 0;
+                lextexts[lex_index].text[MAX_IDENTIFIER_LENGTH] = 0;
             }
 
             if (dont_enter_into_symbol_table)
             {   circle[circle_position].type = DQ_TT;
                 circle[circle_position].value = 0;
                 if (dont_enter_into_symbol_table == -2)
-                    interpret_identifier(circle_position, TRUE);
+                    interpret_identifier(lextexts[lex_index].text, circle_position, TRUE);
                 break;
             }
 
-            interpret_identifier(circle_position, FALSE);
+            interpret_identifier(lextexts[lex_index].text, circle_position, FALSE);
             break;
 
         default:
@@ -1681,24 +1854,25 @@ extern void get_next_token(void)
             for (j=e>>4, k=j+(e&0x0f); j<k; j++)
             {   r = (char *) separators[j];
                 if (r[1]==0)
-                {   *lex_p++=d; *lex_p++=0;
+                {   lexaddc(d);
+                    lexaddc(0);
                     goto SeparatorMatched;
                 }
                 else
                 if (r[2]==0)
                 {   if (*(r+1) == lookahead)
-                    {   *lex_p++=d;
-                        *lex_p++=(*get_next_char)();
-                        *lex_p++=0;
+                    {   lexaddc(d);
+                        lexaddc((*get_next_char)());
+                        lexaddc(0);
                         goto SeparatorMatched;
                     }
                 }
                 else
                 {   if ((*(r+1) == lookahead) && (*(r+2) == lookahead2))
-                    {   *lex_p++=d;
-                        *lex_p++=(*get_next_char)();
-                        *lex_p++=(*get_next_char)();
-                        *lex_p++=0;
+                    {   lexaddc(d);
+                        lexaddc((*get_next_char)());
+                        lexaddc((*get_next_char)());
+                        lexaddc(0);
                         goto SeparatorMatched;
                     }
                 }
@@ -1707,9 +1881,10 @@ extern void get_next_token(void)
             /*  The following contingency never in fact arises with the
                 current set of separators, but might in future  */
 
-            *lex_p++ = d; *lex_p++ = lookahead; *lex_p++ = lookahead2;
-            *lex_p++ = 0;
-            error_named("Unrecognised combination in source:", lex_p);
+            lexaddc(d); lexaddc(lookahead); lexaddc(lookahead2);
+            lexaddc(0);
+            error_named("Unrecognised combination in source:",
+                lextexts[lex_index].text);
             goto StartTokenAgain;
 
             SeparatorMatched:
@@ -1722,15 +1897,15 @@ extern void get_next_token(void)
                 case HASHWDOLLAR_SEP:
                     if (tokeniser_grid[lookahead] == WHITESPACE_CODE)
                     {   error_named("Character expected after",
-                            circle[circle_position].text);
+                            lextexts[lex_index].text);
                         break;
                     }
-                    lex_p--;
-                    *lex_p++ = (*get_next_char)();
+                    lex_pos--;
+                    lexaddc((*get_next_char)());
                     while ((tokeniser_grid[lookahead] == IDENTIFIER_CODE)
                            || (tokeniser_grid[lookahead] == DIGIT_CODE))
-                        *lex_p++ = (*get_next_char)();
-                    *lex_p++ = 0;
+                        lexaddc((*get_next_char)());
+                    lexaddc(0);
                     break;
                 case HASHADOLLAR_SEP:
                 case HASHGDOLLAR_SEP:
@@ -1738,37 +1913,43 @@ extern void get_next_token(void)
                 case HASHHASH_SEP:
                     if (tokeniser_grid[lookahead] != IDENTIFIER_CODE)
                     {   error_named("Alphabetic character expected after",
-                            circle[circle_position].text);
+                            lextexts[lex_index].text);
                         break;
                     }
-                    lex_p--;
+                    lex_pos--;
                     while ((tokeniser_grid[lookahead] == IDENTIFIER_CODE)
                            || (tokeniser_grid[lookahead] == DIGIT_CODE))
-                        *lex_p++ = (*get_next_char)();
-                    *lex_p++ = 0;
+                        lexaddc((*get_next_char)());
+                    lexaddc(0);
                     break;
             }
             break;
     }
 
+    /* We can now assign the text pointer, since the lextext has finished reallocing. */
+    circle[circle_position].text = lextexts[lex_index].text;
     i = circle_position;
 
     ReturnBack:
+    /* We've either parsed a new token or selected a put-back one.
+       i is the circle-position of the token in question. Time to
+       export the token data where higher-level code can find it. */
     token_value = circle[i].value;
     token_type = circle[i].type;
     token_text = circle[i].text;
     if (!returning_a_put_back_token)
     {   set_token_location(circle[i].location);
     }
-    token_contexts[i] = context;
 
     if (tokens_trace_level > 0)
-    {   if (tokens_trace_level == 1)
+    {   if (tokens_trace_level == 1) {
             printf("'%s' ", circle[i].text);
+            if (circle[i].type == EOF_TT) printf("\n");
+        }
         else
-        {   printf("-> "); describe_token(circle[i]);
+        {   printf("-> "); describe_token(&circle[i]);
             printf(" ");
-            if (tokens_trace_level > 2) print_context(token_contexts[i]);
+            if (tokens_trace_level > 2) print_context(circle[i].context);
             printf("\n");
         }
     }
@@ -1783,10 +1964,15 @@ extern void restart_lexer(char *lexical_source, char *name)
     {   circle[i].type = 0;
         circle[i].value = 0;
         circle[i].text = "(if this is ever visible, there is a bug)";
-        token_contexts[i] = 0;
+        circle[i].lextext = -1;
+        circle[i].context = 0;
     }
 
-    lex_p = lexeme_memory;
+    cur_lextexts = 0;
+    /* But we don't touch no_lextexts; those allocated blocks can be reused */
+    lex_index = -1;
+    lex_pos = -1;
+    
     tokens_put_back = 0;
     forerrors_pointer = 0;
     dont_enter_into_symbol_table = FALSE;
@@ -1819,6 +2005,17 @@ extern void restart_lexer(char *lexical_source, char *name)
 
 extern void init_lexer_vars(void)
 {
+    FileStack = NULL;
+    FileStack_max = 0;
+    CF = NULL;
+    CurrentLB = NULL;
+
+    lextexts = NULL;
+    no_lextexts = 0;
+    cur_lextexts = 0;
+    lex_index = -1;
+    lex_pos = -1;
+    
     blank_brief_location.file_index = -1;
     blank_brief_location.line_number = 0;
     blank_brief_location.orig_file_index = 0;
@@ -1846,15 +2043,16 @@ extern void lexer_endpass(void)
 }
 
 extern void lexer_allocate_arrays(void)
-{   int i;
-
-    FileStack = my_malloc(MAX_INCLUSION_DEPTH*sizeof(Sourcefile),
-        "filestack buffer");
-
-    for (i=0; i<MAX_INCLUSION_DEPTH; i++)
-    FileStack[i].buffer = my_malloc(SOURCE_BUFFER_SIZE+4, "source file buffer");
+{
+    initialise_memory_list(&FileStack_memlist,
+        sizeof(Sourcefile), 4, (void**)&FileStack,
+        "source file stack");
+    FileStack_max = 0;
 
-    lexeme_memory = my_malloc(5*MAX_QTEXT_SIZE, "lexeme memory");
+    initialise_memory_list(&lextexts_memlist,
+        sizeof(lextext), 200, (void**)&lextexts,
+        "lexeme texts list");
+    cur_lextexts = 0;
 
     keywords_hash_table = my_calloc(sizeof(int), HASH_TAB_SIZE,
         "keyword hash table");
@@ -1862,16 +2060,13 @@ extern void lexer_allocate_arrays(void)
         "keyword hash end table");
     keywords_data_table = my_calloc(sizeof(int), 3*MAX_KEYWORDS,
         "keyword hashing linked list");
+    
+    local_variable_names = my_calloc(sizeof(identstruct), MAX_LOCAL_VARIABLES-1,
+        "text of local variable names");
     local_variable_hash_table = my_calloc(sizeof(int), HASH_TAB_SIZE,
         "local variable hash table");
-    local_variable_text_table = my_malloc(
-        (MAX_LOCAL_VARIABLES-1)*(MAX_IDENTIFIER_LENGTH+1),
-        "text of local variable names");
-
     local_variable_hash_codes = my_calloc(sizeof(int), MAX_LOCAL_VARIABLES,
         "local variable hash codes");
-    local_variable_texts = my_calloc(sizeof(char *), MAX_LOCAL_VARIABLES,
-        "local variable text pointers");
 
     make_tokeniser_grid();
     make_keywords_tables();
@@ -1894,23 +2089,27 @@ extern void lexer_allocate_arrays(void)
 }
 
 extern void lexer_free_arrays(void)
-{   int i; char *p;
+{   int ix;
+    CF = NULL;
+    CurrentLB = NULL;
 
-    for (i=0; i<MAX_INCLUSION_DEPTH; i++)
-    {   p = FileStack[i].buffer;
-        my_free(&p, "source file buffer");
+    for (ix=0; ix<FileStack_max; ix++) {
+        my_free(&FileStack[ix].buffer, "source file buffer");
     }
-    my_free(&FileStack, "filestack buffer");
-    my_free(&lexeme_memory, "lexeme memory");
+    deallocate_memory_list(&FileStack_memlist);
+
+    for (ix=0; ix<no_lextexts; ix++) {
+        my_free(&lextexts[ix].text, "one lexeme text");
+    }
+    deallocate_memory_list(&lextexts_memlist);
 
     my_free(&keywords_hash_table, "keyword hash table");
     my_free(&keywords_hash_ends_table, "keyword hash end table");
     my_free(&keywords_data_table, "keyword hashing linked list");
-    my_free(&local_variable_hash_table, "local variable hash table");
-    my_free(&local_variable_text_table, "text of local variable names");
 
+    my_free(&local_variable_names, "text of local variable names");
+    my_free(&local_variable_hash_table, "local variable hash table");
     my_free(&local_variable_hash_codes, "local variable hash codes");
-    my_free(&local_variable_texts, "local variable text pointers");
 
     cleanup_token_locations(NULL);
 }
index 6ee0418e0a6aca0390f3ff89ec4e3c0ead4c5b03..2da3249b39b119954e4d732dd47f573b2e034378 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "linker" : For compiling and linking modules                            */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
 #include "header.h"
 
-memory_block link_data_area;
-uchar *link_data_holding_area, *link_data_top;
+uchar *link_data_holding_area;            /* Allocated to link_data_ha_size  */
+static memory_list link_data_holding_area_memlist;
+int32 link_data_ha_size;
+
+uchar *link_data_area;
+static memory_list link_data_area_memlist;
                                           /*  Start, current top, size of    */
 int32 link_data_size;                     /*  link data table being written  */
                                           /*  (holding import/export names)  */
-extern int32 *action_symbol;
 
 /* ------------------------------------------------------------------------- */
 /*   Marker values                                                           */
@@ -220,28 +223,28 @@ static void accept_export(void)
 
     xref_table[IE.symbol_number] = index;
 
-    if (!(sflags[index] & UNKNOWN_SFLAG))
+    if (!(symbols[index].flags & UNKNOWN_SFLAG))
     {   if (IE.module_value == EXPORTAC_MV)
-        {   if ((!(sflags[index] & ACTION_SFLAG))
-                && (stypes[index] != FAKE_ACTION_T))
+        {   if ((!(symbols[index].flags & ACTION_SFLAG))
+                && (symbols[index].type != FAKE_ACTION_T))
                 link_error_named(
 "action name clash with", IE.symbol_name);
         }
         else
-        if (stypes[index] == IE.symbol_type)
+        if (symbols[index].type == IE.symbol_type)
         {   switch(IE.symbol_type)
             {   case CONSTANT_T:
-                    if ((!(svals[index] == IE.symbol_value))
+                    if ((!(symbols[index].value == IE.symbol_value))
                         || (IE.backpatch != 0))
                         link_error_named(
 "program and module give differing values of", IE.symbol_name);
                     break;
                 case INDIVIDUAL_PROPERTY_T:
-                    property_identifier_map[IE.symbol_value] = svals[index];
+                    property_identifier_map[IE.symbol_value] = symbols[index].value;
                     break;
                 case ROUTINE_T:
                     if ((IE.module_value == EXPORTSF_MV)
-                        && (sflags[index] & REPLACE_SFLAG))
+                        && (symbols[index].flags & REPLACE_SFLAG))
                     break;
                 default:
                     sprintf(link_errorm,
@@ -254,7 +257,7 @@ static void accept_export(void)
         else
         {   sprintf(link_errorm,
                     "'%s' has type %s in program but type %s in module",
-                    IE.symbol_name, typename(stypes[index]),
+                    IE.symbol_name, typename(symbols[index].type),
                     typename(IE.symbol_type));
             link_error(link_errorm);
         }
@@ -262,15 +265,18 @@ static void accept_export(void)
     else
     {   if (IE.module_value == EXPORTAC_MV)
         {   IE.symbol_value = no_actions;
-            action_symbol[no_actions++] = index;
+            ensure_memory_list_available(&actions_memlist, no_actions+1);
+            actions[no_actions].symbol = index;
+            actions[no_actions].byte_offset = 0; /* fill in later */
+            no_actions++;
             if (linker_trace_level >= 4)
-                printf("Creating action ##%s\n", (char *) symbs[index]);
+                printf("Creating action ##%s\n", symbols[index].name);
         }
         else
         switch(IE.symbol_type)
         {   case ROUTINE_T:
                 if ((IE.module_value == EXPORTSF_MV)
-                    && (sflags[index] & REPLACE_SFLAG))
+                    && (symbols[index].flags & REPLACE_SFLAG))
                 {   routine_replace[no_rr] = IE.symbol_value;
                     routine_replace_with[no_rr++] = index;
                     return;
@@ -282,7 +288,7 @@ static void accept_export(void)
                 IE.symbol_value += no_objects;
                 break;
             case ARRAY_T:
-                IE.symbol_value += dynamic_array_area_size - (MAX_GLOBAL_VARIABLES*2);
+                IE.symbol_value += dynamic_array_area_size - (MAX_ZCODE_GLOBAL_VARS*2);
                 break;
             case GLOBAL_VARIABLE_T:
                 if (no_globals==233)
@@ -310,21 +316,21 @@ static void accept_export(void)
 
                 break;
         }
-        assign_symbol(index, IE.backpatch*0x10000 + IE.symbol_value,
+        assign_marked_symbol(index, IE.backpatch, IE.symbol_value,
             IE.symbol_type);
-        if (IE.backpatch != 0) sflags[index] |= CHANGE_SFLAG;
-        sflags[index] |= EXPORT_SFLAG;
+        if (IE.backpatch != 0) symbols[index].flags |= CHANGE_SFLAG;
+        symbols[index].flags |= EXPORT_SFLAG;
         if (IE.module_value == EXPORTSF_MV)
-            sflags[index] |= INSF_SFLAG;
+            symbols[index].flags |= INSF_SFLAG;
         if (IE.module_value == EXPORTAC_MV)
-            sflags[index] |= ACTION_SFLAG;
+            symbols[index].flags |= ACTION_SFLAG;
     }
 
     if (IE.module_value == EXPORTAC_MV)
     {   if (linker_trace_level >= 4)
             printf("Map %d '%s' to %d\n",
-                IE.symbol_value, (char *) (symbs[index]), svals[index]);
-        actions_map[map_to] = svals[index];
+                IE.symbol_value, (symbols[index].name), symbols[index].value);
+        actions_map[map_to] = symbols[index].value;
     }
 }
 
@@ -332,22 +338,22 @@ static void accept_import(void)
 {   int32 index;
 
     index = symbol_index(IE.symbol_name, -1);
-    sflags[index] |= USED_SFLAG;
+    symbols[index].flags |= USED_SFLAG;
     xref_table[IE.symbol_number] = index;
 
-    if (!(sflags[index] & UNKNOWN_SFLAG))
+    if (!(symbols[index].flags & UNKNOWN_SFLAG))
     {   switch (IE.symbol_type)
         {
             case GLOBAL_VARIABLE_T:
-                if (stypes[index] != GLOBAL_VARIABLE_T)
+                if (symbols[index].type != GLOBAL_VARIABLE_T)
                     link_error_named(
 "module (wrongly) declared this a variable:", IE.symbol_name);
-                variables_map[IE.symbol_value] = svals[index];
+                variables_map[IE.symbol_value] = symbols[index].value;
                 if (IE.symbol_value < lowest_imported_global_no)
                     lowest_imported_global_no = IE.symbol_value;
                 break;
             default:
-                switch(stypes[index])
+                switch(symbols[index].type)
                 {   case ATTRIBUTE_T:
                         link_error_named(
 "this attribute is undeclared within module:", IE.symbol_name);; break;
@@ -374,7 +380,7 @@ static void accept_import(void)
     {   switch (IE.symbol_type)
         {
             case GLOBAL_VARIABLE_T:
-                if (stypes[index] != GLOBAL_VARIABLE_T)
+                if (symbols[index].type != GLOBAL_VARIABLE_T)
                     link_error_named(
                 "Module tried to import a Global variable not defined here:",
                         IE.symbol_name);
@@ -463,11 +469,11 @@ static int32 backpatch_backpatch(int32 v)
             break;
 
         case ARRAY_MV:
-            if (v < (MAX_GLOBAL_VARIABLES*2))
+            if (v < (MAX_ZCODE_GLOBAL_VARS*2))
             {   v = 2*(variables_map[v/2 + 16] - 16);
             }
             else
-            {   v += dynamic_array_area_size - (MAX_GLOBAL_VARIABLES*2);
+            {   v += dynamic_array_area_size - (MAX_ZCODE_GLOBAL_VARS*2);
             }
             break;
 
@@ -512,12 +518,12 @@ static void backpatch_module_image(uchar *p,
 /*   The main routine: linking in a module with the given filename.          */
 /* ------------------------------------------------------------------------- */
 
-char current_module_filename[128];
+char current_module_filename[PATHLEN];
 
 void link_module(char *given_filename)
 {   FILE *fin;
     int record_type;
-    char filename[128];
+    char filename[PATHLEN];
     uchar *p, p0[64];
     int32 last, i, j, k, l, m, vn, len, size, link_offset, module_size, map,
           max_property_identifier, symbols_base = no_symbols;
@@ -638,7 +644,7 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
     no_rr = 0;
 
     if ((linker_trace_level>=1) || transcript_switch)
-    {   char link_banner[128];
+    {   char link_banner[PATHLEN+128];
         sprintf(link_banner,
             "[Linking release %d.%c%c%c%c%c%c of module '%s' (size %dK)]",
             p[2]*256 + p[3], p[18], p[19], p[20], p[21], p[22], p[23],
@@ -681,7 +687,7 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
         if (((record_type == EXPORT_MV) || (record_type == EXPORTSF_MV))
             && (IE.symbol_type == INDIVIDUAL_PROPERTY_T))
         {   int32 si = symbol_index(IE.symbol_name, -1);
-            property_identifier_map[IE.symbol_value] = svals[si];
+            property_identifier_map[IE.symbol_value] = symbols[si].value;
         }
         switch(record_type)
         {   case EXPORT_MV:
@@ -706,7 +712,7 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
         for (i=0; i<module_map[6]; i++)
         {   if (xref_table[i] != -1)
                 printf("module %4d -> story file '%s'\n", i,
-                    (char *) symbs[xref_table[i]]);
+                    symbols[xref_table[i]].name);
         }
     }
 
@@ -728,14 +734,14 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
     /* (6) Backpatch the backpatch markers attached to exported symbols  */
 
     for (i=symbols_base; i<no_symbols; i++)
-    {   if ((sflags[i] & CHANGE_SFLAG) && (sflags[i] & EXPORT_SFLAG))
-        {   backpatch_marker = svals[i]/0x10000;
-            j = svals[i] % 0x10000;
+    {   if ((symbols[i].flags & CHANGE_SFLAG) && (symbols[i].flags & EXPORT_SFLAG))
+        {   backpatch_marker = symbols[i].marker;
+            j = symbols[i].value % 0x10000;
 
             j = backpatch_backpatch(j);
 
-            svals[i] = backpatch_marker*0x10000 + j;
-            if (backpatch_marker == 0) sflags[i] &= (~(CHANGE_SFLAG));
+            symbols[i].value = j;
+            if (backpatch_marker == 0) symbols[i].flags &= (~(CHANGE_SFLAG));
         }
     }
 
@@ -753,13 +759,11 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
                 backpatch_module_image(p, marker_value, ZCODE_ZA, offset);
                 break;
             default:
+                ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+3);
                 backpatch_module_image(p, marker_value, ZCODE_ZA, offset);
-                write_byte_to_memory_block(&zcode_backpatch_table,
-                    zcode_backpatch_size++, backpatch_marker);
-                write_byte_to_memory_block(&zcode_backpatch_table,
-                    zcode_backpatch_size++, (offset + zmachine_pc)/256);
-                write_byte_to_memory_block(&zcode_backpatch_table,
-                    zcode_backpatch_size++, (offset + zmachine_pc)%256);
+                zcode_backpatch_table[zcode_backpatch_size++] = backpatch_marker;
+                zcode_backpatch_table[zcode_backpatch_size++] = (offset + zmachine_pc)/256;
+                zcode_backpatch_table[zcode_backpatch_size++] = (offset + zmachine_pc)%256;
                 break;
         }
     }
@@ -786,11 +790,11 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
                     case INDIVIDUAL_PROP_ZA:
                         offset += individuals_length; break;
                     case DYNAMIC_ARRAY_ZA:
-                        if (offset < (MAX_GLOBAL_VARIABLES*2))
+                        if (offset < (MAX_ZCODE_GLOBAL_VARS*2))
                         {   offset = 2*(variables_map[offset/2 + 16] - 16);
                         }
                         else
-                        {   offset += dynamic_array_area_size - (MAX_GLOBAL_VARIABLES*2);
+                        {   offset += dynamic_array_area_size - (MAX_ZCODE_GLOBAL_VARS*2);
                         }
                         break;
                 }
@@ -821,17 +825,16 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
 
     /* (10) Glue in the dynamic array data */
 
-    i = m_static_offset - m_vars_offset - MAX_GLOBAL_VARIABLES*2;
-    if (dynamic_array_area_size + i >= MAX_STATIC_DATA)
-        memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA);
+    i = m_static_offset - m_vars_offset - MAX_ZCODE_GLOBAL_VARS*2;
+    ensure_memory_list_available(&dynamic_array_area_memlist, dynamic_array_area_size + i);
 
     if (linker_trace_level >= 2)
         printf("Inserting dynamic array area, %04x to %04x, at %04x\n",
-            m_vars_offset + MAX_GLOBAL_VARIABLES*2, m_static_offset,
+            m_vars_offset + MAX_ZCODE_GLOBAL_VARS*2, m_static_offset,
             variables_offset + dynamic_array_area_size);
     for (k=0;k<i;k++)
     {   dynamic_array_area[dynamic_array_area_size+k]
-            = p[m_vars_offset+MAX_GLOBAL_VARIABLES*2+k];
+            = p[m_vars_offset+MAX_ZCODE_GLOBAL_VARS*2+k];
     }
     dynamic_array_area_size+=i;
 
@@ -841,13 +844,11 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
       printf("Inserting code area, %04x to %04x, at code offset %04x (+%04x)\n",
         m_code_offset, m_strs_offset, code_offset, zmachine_pc);
 
+    ensure_memory_list_available(&zcode_area_memlist, zmachine_pc + (m_strs_offset - m_code_offset));
+    
     for (k=m_code_offset;k<m_strs_offset;k++)
-    {   if (temporary_files_switch)
-        {   fputc(p[k],Temp2_fp);
-            zmachine_pc++;
-        }
-        else
-            write_byte_to_memory_block(&zcode_area, zmachine_pc++, p[k]);
+    {
+        zcode_area[zmachine_pc++] = p[k];
     }
 
     /* (12) Glue in the static strings area */
@@ -857,14 +858,10 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename);
 at strings offset %04x (+%04x)\n",
         m_strs_offset, link_offset, strings_offset,
         static_strings_extent);
+    ensure_memory_list_available(&static_strings_area_memlist, static_strings_extent+link_offset-m_strs_offset);
     for (k=m_strs_offset;k<link_offset;k++)
-    {   if (temporary_files_switch)
-        {   fputc(p[k], Temp1_fp);
-            static_strings_extent++;
-        }
-        else
-            write_byte_to_memory_block(&static_strings_area,
-                    static_strings_extent++, p[k]);
+    {
+        static_strings_area[static_strings_extent++] = p[k];
     }
 
     /* (13) Append the class object-numbers table: note that modules
@@ -875,9 +872,11 @@ at strings offset %04x (+%04x)\n",
     {   j = p[i]*256 + p[i+1]; i+=2;
         if (j == 0) break;
 
-        class_object_numbers[no_classes] = j + no_objects;
+        ensure_memory_list_available(&class_info_memlist, no_classes+1);
+        
+        class_info[no_classes].object_number = j + no_objects;
         j = p[i]*256 + p[i+1]; i+=2;
-        class_begins_at[no_classes++] = j + properties_table_size;
+        class_info[no_classes++].begins_at = j + properties_table_size;
 
     } while (TRUE);
 
@@ -887,7 +886,9 @@ at strings offset %04x (+%04x)\n",
         printf("Joining on object tree of size %d\n", m_no_objects);
 
     for (i=0, k=no_objects, last=m_props_offset;i<m_no_objects;i++)
-    {   objectsz[no_objects].atts[0]=p[m_objs_offset+14*i];
+    {
+        ensure_memory_list_available(&objectsz_memlist, no_objects+1);
+        objectsz[no_objects].atts[0]=p[m_objs_offset+14*i];
         objectsz[no_objects].atts[1]=p[m_objs_offset+14*i+1];
         objectsz[no_objects].atts[2]=p[m_objs_offset+14*i+2];
         objectsz[no_objects].atts[3]=p[m_objs_offset+14*i+3];
@@ -935,13 +936,12 @@ at strings offset %04x (+%04x)\n",
     /* (15) Glue on the properties */
 
     if (last>m_props_offset)
-    {   i = m_static_offset - m_vars_offset - MAX_GLOBAL_VARIABLES*2;
-        if (dynamic_array_area_size + i >= MAX_STATIC_DATA)
-            memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA);
+    {   i = m_static_offset - m_vars_offset - MAX_ZCODE_GLOBAL_VARS*2;
 
         if (linker_trace_level >= 2)
             printf("Inserting object properties area, %04x to %04x, at +%04x\n",
                 m_props_offset, last, properties_table_size);
+        ensure_memory_list_available(&properties_table_memlist, properties_table_size+last-m_props_offset);
         for (k=0;k<last-m_props_offset;k++)
             properties_table[properties_table_size++] = p[m_props_offset+k];
     }
@@ -954,9 +954,7 @@ at strings offset %04x (+%04x)\n",
     /* (17) Append the individual property values table */
 
     i = m_individuals_length;
-    if (individuals_length + i >= MAX_INDIV_PROP_TABLE_SIZE)
-        memoryerror("MAX_INDIV_PROP_TABLE_SIZE",
-            MAX_INDIV_PROP_TABLE_SIZE);
+    ensure_memory_list_available(&individuals_table_memlist, individuals_length + i);
 
     if (linker_trace_level >= 2)
       printf("Inserting individual prop tables area, %04x to %04x, at +%04x\n",
@@ -987,23 +985,19 @@ at strings offset %04x (+%04x)\n",
 /* ------------------------------------------------------------------------- */
 
 static void write_link_byte(int x)
-{   *link_data_top=(unsigned char) x; link_data_top++; link_data_size++;
-    if (subtract_pointers(link_data_top,link_data_holding_area)
-        >= MAX_LINK_DATA_SIZE)
-    {   memoryerror("MAX_LINK_DATA_SIZE",MAX_LINK_DATA_SIZE);
-    }
+{
+    ensure_memory_list_available(&link_data_holding_area_memlist, link_data_ha_size+1);
+    link_data_holding_area[link_data_ha_size] = (unsigned char) x;
+    link_data_ha_size++; link_data_size++;
 }
 
 extern void flush_link_data(void)
 {   int32 i, j;
-    j = subtract_pointers(link_data_top, link_data_holding_area);
-    if (temporary_files_switch)
-        for (i=0;i<j;i++) fputc(link_data_holding_area[i], Temp3_fp);
-    else
-        for (i=0;i<j;i++)
-            write_byte_to_memory_block(&link_data_area, link_data_size-j+i,
-            link_data_holding_area[i]);
-    link_data_top=link_data_holding_area;
+    j = link_data_ha_size;
+    ensure_memory_list_available(&link_data_area_memlist, link_data_size);
+    for (i=0;i<j;i++)
+        link_data_area[link_data_size-j+i] = link_data_holding_area[i];
+    link_data_ha_size = 0;
 }
 
 static void write_link_word(int32 x)
@@ -1026,23 +1020,23 @@ static void export_symbols(void)
     for (symbol_number = 0; symbol_number < no_symbols; symbol_number++)
     {   int export_flag = FALSE, import_flag = FALSE;
 
-        if (stypes[symbol_number]==GLOBAL_VARIABLE_T)
-        {   if (svals[symbol_number] < LOWEST_SYSTEM_VAR_NUMBER)
-            {   if (sflags[symbol_number] & IMPORT_SFLAG)
+        if (symbols[symbol_number].type==GLOBAL_VARIABLE_T)
+        {   if (symbols[symbol_number].value < LOWEST_SYSTEM_VAR_NUMBER)
+            {   if (symbols[symbol_number].flags & IMPORT_SFLAG)
                     import_flag = TRUE;
                 else
-                    if (!(sflags[symbol_number] & SYSTEM_SFLAG))
+                    if (!(symbols[symbol_number].flags & SYSTEM_SFLAG))
                         export_flag = TRUE;
             }
         }
         else
-        {   if (!(sflags[symbol_number] & SYSTEM_SFLAG))
-            {   if (sflags[symbol_number] & UNKNOWN_SFLAG)
-                {   if (sflags[symbol_number] & IMPORT_SFLAG)
+        {   if (!(symbols[symbol_number].flags & SYSTEM_SFLAG))
+            {   if (symbols[symbol_number].flags & UNKNOWN_SFLAG)
+                {   if (symbols[symbol_number].flags & IMPORT_SFLAG)
                         import_flag = TRUE;
                 }
                 else
-                switch(stypes[symbol_number])
+                switch(symbols[symbol_number].type)
                 {   case LABEL_T:
                     case ATTRIBUTE_T:
                     case PROPERTY_T:
@@ -1058,27 +1052,27 @@ static void export_symbols(void)
         {   if (linker_trace_level >= 1)
             {   IE.module_value = EXPORT_MV;
                 IE.symbol_number = symbol_number;
-                IE.symbol_type = stypes[symbol_number];
-                IE.symbol_value = svals[symbol_number];
-                IE.symbol_name = (char *) (symbs[symbol_number]);
+                IE.symbol_type = symbols[symbol_number].type;
+                IE.symbol_value = symbols[symbol_number].value;
+                IE.symbol_name = (symbols[symbol_number].name);
                 describe_importexport(&IE);
             }
 
-            if (sflags[symbol_number] & ACTION_SFLAG)
+            if (symbols[symbol_number].flags & ACTION_SFLAG)
                 write_link_byte(EXPORTAC_MV);
             else
-            if (sflags[symbol_number] & INSF_SFLAG)
+            if (symbols[symbol_number].flags & INSF_SFLAG)
                 write_link_byte(EXPORTSF_MV);
             else
                 write_link_byte(EXPORT_MV);
 
             write_link_word(symbol_number);
-            write_link_byte(stypes[symbol_number]);
-            if (sflags[symbol_number] & CHANGE_SFLAG)
-                 write_link_byte(svals[symbol_number] / 0x10000);
+            write_link_byte(symbols[symbol_number].type);
+            if (symbols[symbol_number].flags & CHANGE_SFLAG)
+                 write_link_byte(symbols[symbol_number].marker);
             else write_link_byte(0);
-            write_link_word(svals[symbol_number] % 0x10000);
-            write_link_string((char *) (symbs[symbol_number]));
+            write_link_word(symbols[symbol_number].value % 0x10000);
+            write_link_string((symbols[symbol_number].name));
             flush_link_data();
         }
 
@@ -1086,17 +1080,17 @@ static void export_symbols(void)
         {   if (linker_trace_level >= 1)
             {   IE.module_value = IMPORT_MV;
                 IE.symbol_number = symbol_number;
-                IE.symbol_type = stypes[symbol_number];
-                IE.symbol_value = svals[symbol_number];
-                IE.symbol_name = (char *) (symbs[symbol_number]);
+                IE.symbol_type = symbols[symbol_number].type;
+                IE.symbol_value = symbols[symbol_number].value;
+                IE.symbol_name = (symbols[symbol_number].name);
                 describe_importexport(&IE);
             }
 
             write_link_byte(IMPORT_MV);
             write_link_word(symbol_number);
-            write_link_byte(stypes[symbol_number]);
-            write_link_word(svals[symbol_number]);
-            write_link_string((char *) (symbs[symbol_number]));
+            write_link_byte(symbols[symbol_number].type);
+            write_link_word(symbols[symbol_number].value);
+            write_link_string((symbols[symbol_number].name));
             flush_link_data();
         }
     }
@@ -1109,10 +1103,10 @@ static void export_symbols(void)
 int mv_vref=LOWEST_SYSTEM_VAR_NUMBER-1;
 
 void import_symbol(int32 symbol_number)
-{   sflags[symbol_number] |= IMPORT_SFLAG;
-    switch(stypes[symbol_number])
+{   symbols[symbol_number].flags |= IMPORT_SFLAG;
+    switch(symbols[symbol_number].type)
     {   case GLOBAL_VARIABLE_T:
-            assign_symbol(symbol_number, mv_vref--, stypes[symbol_number]);
+            assign_symbol(symbol_number, mv_vref--, symbols[symbol_number].type);
             break;
     }
 }
@@ -1123,11 +1117,13 @@ void import_symbol(int32 symbol_number)
 
 extern void init_linker_vars(void)
 {   link_data_size = 0;
-    initialise_memory_block(&link_data_area);
+    link_data_area = NULL;
+    link_data_ha_size = 0;
+    link_data_holding_area = NULL;
 }
 
 extern void linker_begin_pass(void)
-{   link_data_top = link_data_holding_area;
+{   link_data_ha_size = 0;
 }
 
 extern void linker_endpass(void)
@@ -1137,17 +1133,20 @@ extern void linker_endpass(void)
 }
 
 extern void linker_allocate_arrays(void)
-{   if (!module_switch)
-        link_data_holding_area
-            = my_malloc(64, "link data holding area");
-    else
-        link_data_holding_area
-            = my_malloc(MAX_LINK_DATA_SIZE, "link data holding area");
+{
+    int initlinksize = (module_switch ? 2000 : 0);
+    initialise_memory_list(&link_data_holding_area_memlist,
+        sizeof(uchar), initlinksize, (void**)&link_data_holding_area,
+        "link data holding area");
+    initialise_memory_list(&link_data_area_memlist,
+        sizeof(uchar), 128, (void**)&link_data_area,
+        "link data area");
 }
 
 extern void linker_free_arrays(void)
-{   my_free(&link_data_holding_area, "link data holding area");
-    deallocate_memory_block(&link_data_area);
+{
+    deallocate_memory_list(&link_data_holding_area_memlist);
+    deallocate_memory_list(&link_data_area_memlist);
 }
 
 /* ========================================================================= */
index a160788d9b51b3195374afa3d7e5c17e6ed2a0a0..bf2eec057c8caa16901d6ff45ab248ff82149eb7 100644 (file)
@@ -1,9 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "memory" : Memory management and ICL memory setting commands            */
-/*              (For "memoryerror", see "errors.c")                          */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
 #include "header.h"
 
-int32 malloced_bytes=0;                /* Total amount of memory allocated   */
+size_t malloced_bytes=0;               /* Total amount of memory allocated   */
+
+/* Wrappers for malloc(), realloc(), etc.
+
+   Note that all of these functions call memory_out_error() on failure.
+   This is a fatal error and does not return. However, we check my_malloc()
+   return values anyway as a matter of good habit.
+ */
 
 #ifdef PC_QUICKC
 
-extern void *my_malloc(int32 size, char *whatfor)
+extern void *my_malloc(size_t size, char *whatfor)
 {   char _huge *c;
     if (memout_switch)
         printf("Allocating %ld bytes for %s\n",size,whatfor);
     if (size==0) return(NULL);
-    c=(char _huge *)halloc(size,1); malloced_bytes+=size;
+    c=(char _huge *)halloc(size,1);
+    malloced_bytes+=size;
     if (c==0) memory_out_error(size, 1, whatfor);
     return(c);
 }
 
-extern void my_realloc(void *pointer, int32 oldsize, int32 size, 
+extern void my_realloc(void *pointer, size_t oldsize, size_t size, 
     char *whatfor)
 {   char _huge *c;
     if (size==0) {
         my_free(pointer, whatfor);
         return;
     }
-    c=halloc(size,1); malloced_bytes+=size;
+    c=halloc(size,1);
+    malloced_bytes+=(size-oldsize);
     if (c==0) memory_out_error(size, 1, whatfor);
     if (memout_switch)
-        printf("Increasing allocation to %ld bytes for %s was (%08lx) \
-now (%08lx)\n",
-            (long int) size,whatfor,(long int) (*(int **)pointer), 
+        printf("Increasing allocation from %ld to %ld bytes for %s was (%08lx) now (%08lx)\n",
+            (long int) oldsize, (long int) size, whatfor,
+            (long int) (*(int **)pointer), 
             (long int) c);
     memcpy(c, *(int **)pointer, MIN(oldsize, size));
     hfree(*(int **)pointer);
     *(int **)pointer = c;
 }
 
-extern void *my_calloc(int32 size, int32 howmany, char *whatfor)
+extern void *my_calloc(size_t size, size_t howmany, char *whatfor)
 {   void _huge *c;
     if (memout_switch)
         printf("Allocating %d bytes: array (%ld entries size %ld) for %s\n",
             size*howmany,howmany,size,whatfor);
     if ((size*howmany) == 0) return(NULL);
-    c=(void _huge *)halloc(howmany*size,1); malloced_bytes+=size*howmany;
+    c=(void _huge *)halloc(howmany*size,1);
+    malloced_bytes+=size*howmany;
     if (c==0) memory_out_error(size, howmany, whatfor);
     return(c);
 }
 
-extern void my_recalloc(void *pointer, int32 size, int32 oldhowmany, 
+extern void my_recalloc(void *pointer, size_t size, size_t oldhowmany, 
     int32 howmany, char *whatfor)
 {   void _huge *c;
     if (size*howmany==0) {
         my_free(pointer, whatfor);
         return;
     }
-    c=(void _huge *)halloc(size*howmany,1); malloced_bytes+=size*howmany;
+    c=(void _huge *)halloc(size*howmany,1);
+    malloced_bytes+=size*(howmany-oldhowmany);
     if (c==0) memory_out_error(size, howmany, whatfor);
     if (memout_switch)
-        printf("Increasing allocation to %ld bytes: array (%ld entries size %ld) \
-for %s was (%08lx) now (%08lx)\n",
+        printf("Increasing allocation from %ld to %ld bytes: array (%ld entries size %ld) for %s was (%08lx) now (%08lx)\n",
+            ((long int)size) * ((long int)oldhowmany),
             ((long int)size) * ((long int)howmany),
-            (long int)howmany,(long int)size,whatfor,
+            (long int)howmany, (long int)size, whatfor,
             (long int) *(int **)pointer, (long int) c);
     memcpy(c, *(int **)pointer, MIN(size*oldhowmany, size*howmany));
     hfree(*(int **)pointer);
@@ -88,10 +98,11 @@ for %s was (%08lx) now (%08lx)\n",
 
 #else
 
-extern void *my_malloc(int32 size, char *whatfor)
+extern void *my_malloc(size_t size, char *whatfor)
 {   char *c;
     if (size==0) return(NULL);
-    c=malloc((size_t) size); malloced_bytes+=size;
+    c=malloc(size);
+    malloced_bytes+=size;
     if (c==0) memory_out_error(size, 1, whatfor);
     if (memout_switch)
         printf("Allocating %ld bytes for %s at (%08lx)\n",
@@ -99,27 +110,29 @@ extern void *my_malloc(int32 size, char *whatfor)
     return(c);
 }
 
-extern void my_realloc(void *pointer, int32 oldsize, int32 size, 
+extern void my_realloc(void *pointer, size_t oldsize, size_t size, 
     char *whatfor)
 {   void *c;
     if (size==0) {
         my_free(pointer, whatfor);
         return;
     }
-    c=realloc(*(int **)pointer, (size_t) size); malloced_bytes+=size;
+    c=realloc(*(int **)pointer,  size);
+    malloced_bytes+=(size-oldsize);
     if (c==0) memory_out_error(size, 1, whatfor);
     if (memout_switch)
-        printf("Increasing allocation to %ld bytes for %s was (%08lx) \
-now (%08lx)\n",
-            (long int) size,whatfor,(long int) (*(int **)pointer), 
+        printf("Increasing allocation from %ld to %ld bytes for %s was (%08lx) now (%08lx)\n",
+            (long int) oldsize, (long int) size, whatfor,
+            (long int) (*(int **)pointer), 
             (long int) c);
     *(int **)pointer = c;
 }
 
-extern void *my_calloc(int32 size, int32 howmany, char *whatfor)
+extern void *my_calloc(size_t size, size_t howmany, char *whatfor)
 {   void *c;
     if (size*howmany==0) return(NULL);
-    c=calloc(howmany,(size_t) size); malloced_bytes+=size*howmany;
+    c=calloc(howmany, size);
+    malloced_bytes+=size*howmany;
     if (c==0) memory_out_error(size, howmany, whatfor);
     if (memout_switch)
         printf("Allocating %ld bytes: array (%ld entries size %ld) \
@@ -130,21 +143,21 @@ for %s at (%08lx)\n",
     return(c);
 }
 
-extern void my_recalloc(void *pointer, int32 size, int32 oldhowmany, 
-    int32 howmany, char *whatfor)
+extern void my_recalloc(void *pointer, size_t size, size_t oldhowmany, 
+    size_t howmany, char *whatfor)
 {   void *c;
     if (size*howmany==0) {
         my_free(pointer, whatfor);
         return;
     }
-    c=realloc(*(int **)pointer, (size_t)size*(size_t)howmany); 
-    malloced_bytes+=size*howmany;
+    c=realloc(*(int **)pointer, size*howmany); 
+    malloced_bytes+=size*(howmany-oldhowmany);
     if (c==0) memory_out_error(size, howmany, whatfor);
     if (memout_switch)
-        printf("Increasing allocation to %ld bytes: array (%ld entries size %ld) \
-for %s was (%08lx) now (%08lx)\n",
+        printf("Increasing allocation from %ld to %ld bytes: array (%ld entries size %ld) for %s was (%08lx) now (%08lx)\n",
+            ((long int)size) * ((long int)oldhowmany),
             ((long int)size) * ((long int)howmany),
-            (long int)howmany,(long int)size,whatfor,
+            (long int)howmany, (long int)size, whatfor,
             (long int) *(int **)pointer, (long int) c);
     *(int **)pointer = c;
 }
@@ -167,140 +180,118 @@ extern void my_free(void *pointer, char *whatitwas)
 }
 
 /* ------------------------------------------------------------------------- */
-/*   Extensible blocks of memory, providing a kind of RAM disc as an         */
-/*   alternative to the temporary files option                               */
+/*   A dynamic memory array. This grows as needed (but never shrinks).       */
+/*   Call ensure_memory_list_available(N) before accessing array item N-1.   */
+/*                                                                           */
+/*   whatfor must be a static string describing the list. initalloc is       */
+/*   (optionally) the number of items to allocate right away.                */
 /*                                                                           */
-/*   The allocation is slightly confusing. A block can store up to 72        */
-/*   chunks, which are allocated as needed when data is written. (Data does  */
-/*   not have to be written in order, but you should not try to read a byte  */
-/*   before writing it.) The size of a chunk is defined by ALLOC_CHUNK_SIZE. */
-/*   So any block can store any amount of data, but you increase the limit   */
-/*   (for all blocks) by increasing ALLOC_CHUNK_SIZE, not the number of      */
-/*   chunks.                                                                 */
+/*   You typically initialise this with extpointer referring to an array of  */
+/*   structs or whatever type you need. Whenever the memory list grows, the  */
+/*   external array will be updated to refer to the new data.                */
+/*                                                                           */
+/*   Add "#define DEBUG_MEMLISTS" to allocate exactly the number of items    */
+/*   needed, rather than increasing allocations exponentially. This is very  */
+/*   slow but it lets us track down array overruns.                          */
 /* ------------------------------------------------------------------------- */
 
-static char chunk_name_buffer[60];
-static char *chunk_name(memory_block *MB, int no)
-{   char *p = "(unknown)";
-    if (MB == &static_strings_area) p = "static strings area";
-    if (MB == &zcode_area)          p = "Z-code area";
-    if (MB == &link_data_area)      p = "link data area";
-    if (MB == &zcode_backpatch_table) p = "Z-code backpatch table";
-    if (MB == &staticarray_backpatch_table) p = "Static array backpatch table";
-    if (MB == &zmachine_backpatch_table) p = "Z-machine backpatch table";
-    sprintf(chunk_name_buffer, "%s chunk %d", p, no);
-    return(chunk_name_buffer);
-}
+void initialise_memory_list(memory_list *ML, size_t itemsize, size_t initalloc, void **extpointer, char *whatfor)
+{
+    #ifdef DEBUG_MEMLISTS
+    initalloc = 0;          /* No initial allocation */
+    #endif
+    
+    ML->whatfor = whatfor;
+    ML->itemsize = itemsize;
+    ML->count = 0;
+    ML->data = NULL;
+    ML->extpointer = extpointer;
 
-extern void initialise_memory_block(memory_block *MB)
-{   int i;
-    MB->chunks = 0;
-    for (i=0; i<72; i++) MB->chunk[i] = NULL;
-    MB->extent_of_last = 0;
-    MB->write_pos = 0;
-}
+    if (initalloc) {
+        ML->count = initalloc;
+        ML->data = my_calloc(ML->itemsize, ML->count, ML->whatfor);
+        if (ML->data == NULL) return;
+    }
 
-extern void deallocate_memory_block(memory_block *MB)
-{   int i;
-    for (i=0; i<72; i++)
-        if (MB->chunk[i] != NULL)
-            my_free(&(MB->chunk[i]), chunk_name(MB, i));
-    MB->chunks = 0;
-    MB->extent_of_last = 0;
+    if (ML->extpointer)
+        *(ML->extpointer) = ML->data;
 }
 
-extern int read_byte_from_memory_block(memory_block *MB, int32 index)
-{   uchar *p;
-    p = MB->chunk[index/ALLOC_CHUNK_SIZE];
-    if (p == NULL)
-    {   compiler_error_named("memory: read from unwritten byte in",
-            chunk_name(MB, index/ALLOC_CHUNK_SIZE));
-        return 0;
-    }
-    return p[index % ALLOC_CHUNK_SIZE];
+void deallocate_memory_list(memory_list *ML)
+{
+    ML->itemsize = 0;
+    ML->count = 0;
+    
+    if (ML->data)
+        my_free(&(ML->data), ML->whatfor);
+
+    if (ML->extpointer)
+        *(ML->extpointer) = NULL;
+    ML->extpointer = NULL;
 }
 
-extern void write_byte_to_memory_block(memory_block *MB, int32 index, int value)
-{   uchar *p; int ch = index/ALLOC_CHUNK_SIZE;
-    if (ch < 0)
-    {   compiler_error_named("memory: negative index to", chunk_name(MB, 0));
+/* After this is called, at least count items will be available in the list.
+   That is, you can freely access array[0] through array[count-1]. */
+void ensure_memory_list_available(memory_list *ML, size_t count)
+{
+    size_t oldcount;
+    
+    if (ML->itemsize == 0) {
+        /* whatfor is also null! */
+        compiler_error("memory: attempt to access uninitialized memory_list");
         return;
     }
-    if (ch >= 72) memoryerror("ALLOC_CHUNK_SIZE", ALLOC_CHUNK_SIZE);
 
-    if (MB->chunk[ch] == NULL)
-    {   int i;
-        MB->chunk[ch] = my_malloc(ALLOC_CHUNK_SIZE, chunk_name(MB, ch));
-        p = MB->chunk[ch];
-        for (i=0; i<ALLOC_CHUNK_SIZE; i++) p[i] = 255;
+    if (ML->count >= count) {
+        return;
     }
 
-    p = MB->chunk[ch];
-    p[index % ALLOC_CHUNK_SIZE] = value;
+    oldcount = ML->count;
+    ML->count = 2*count+8;  /* Allow headroom for future growth */
+    
+    #ifdef DEBUG_MEMLISTS
+    ML->count = count;      /* No headroom */
+    #endif
+    
+    if (ML->data == NULL)
+        ML->data = my_calloc(ML->itemsize, ML->count, ML->whatfor);
+    else
+        my_recalloc(&(ML->data), ML->itemsize, oldcount, ML->count, ML->whatfor);
+    if (ML->data == NULL) return;
+
+    if (ML->extpointer)
+        *(ML->extpointer) = ML->data;
 }
 
 /* ------------------------------------------------------------------------- */
 /*   Where the memory settings are declared as variables                     */
 /* ------------------------------------------------------------------------- */
 
-int MAX_QTEXT_SIZE;
-int MAX_SYMBOLS;
-int SYMBOLS_CHUNK_SIZE;
 int HASH_TAB_SIZE;
-int MAX_OBJECTS;
-int MAX_ARRAYS;
-int MAX_ACTIONS;
-int MAX_ADJECTIVES;
-int MAX_DICT_ENTRIES;
-int MAX_STATIC_DATA;
-int MAX_PROP_TABLE_SIZE;
 int MAX_ABBREVS;
 int MAX_DYNAMIC_STRINGS;
-int MAX_EXPRESSION_NODES;
-int MAX_VERBS;
-int MAX_VERBSPACE;
-int MAX_LABELS;
-int MAX_LINESPACE;
-int32 MAX_STATIC_STRINGS;
-int32 MAX_ZCODE_SIZE;
-int MAX_LOW_STRINGS;
-int32 MAX_TRANSCRIPT_SIZE;
-int MAX_CLASSES;
-int32 MAX_LINK_DATA_SIZE;
-int MAX_INCLUSION_DEPTH;
-int MAX_SOURCE_FILES;
-int32 MAX_INDIV_PROP_TABLE_SIZE;
-int32 MAX_OBJ_PROP_TABLE_SIZE;
-int MAX_OBJ_PROP_COUNT;
 int MAX_LOCAL_VARIABLES;
-int MAX_GLOBAL_VARIABLES;
 int DICT_WORD_SIZE; /* number of characters in a dict word */
 int DICT_CHAR_SIZE; /* (glulx) 1 for one-byte chars, 4 for Unicode chars */
 int DICT_WORD_BYTES; /* DICT_WORD_SIZE*DICT_CHAR_SIZE */
 int ZCODE_HEADER_EXT_WORDS; /* (zcode 1.0) requested header extension size */
 int ZCODE_HEADER_FLAGS_3; /* (zcode 1.1) value to place in Flags 3 word */
+int ZCODE_LESS_DICT_DATA; /* (zcode) use 2 data bytes per dict word instead of 3 */
 int NUM_ATTR_BYTES;
 int GLULX_OBJECT_EXT_BYTES; /* (glulx) extra bytes for each object record */
-int32 MAX_NUM_STATIC_STRINGS;
-int32 MAX_UNICODE_CHARS;
 int32 MAX_STACK_SIZE;
 int32 MEMORY_MAP_EXTENSION;
-int ALLOC_CHUNK_SIZE;
 int WARN_UNUSED_ROUTINES; /* 0: no, 1: yes except in system files, 2: yes always */
 int OMIT_UNUSED_ROUTINES; /* 0: no, 1: yes */
+int STRIP_UNREACHABLE_LABELS; /* 0: no, 1: yes (default) */
 int TRANSCRIPT_FORMAT; /* 0: classic, 1: prefixed */
 
 /* The way memory sizes are set causes great nuisance for those parameters
    which have different defaults under Z-code and Glulx. We have to get
    the defaults right whether the user sets "-G $HUGE" or "$HUGE -G". 
    And an explicit value set by the user should override both defaults. */
-static int32 MAX_ZCODE_SIZE_z, MAX_ZCODE_SIZE_g;
-static int MAX_PROP_TABLE_SIZE_z, MAX_PROP_TABLE_SIZE_g;
-static int MAX_GLOBAL_VARIABLES_z, MAX_GLOBAL_VARIABLES_g;
-static int MAX_LOCAL_VARIABLES_z, MAX_LOCAL_VARIABLES_g;
 static int DICT_WORD_SIZE_z, DICT_WORD_SIZE_g;
 static int NUM_ATTR_BYTES_z, NUM_ATTR_BYTES_g;
-static int ALLOC_CHUNK_SIZE_z, ALLOC_CHUNK_SIZE_g;
 static int MAX_DYNAMIC_STRINGS_z, MAX_DYNAMIC_STRINGS_g;
 
 /* ------------------------------------------------------------------------- */
@@ -312,227 +303,38 @@ static void list_memory_sizes(void)
     printf("|  %25s = %-7s |\n","Memory setting","Value");
     printf("+--------------------------------------+\n");
     printf("|  %25s = %-7d |\n","MAX_ABBREVS",MAX_ABBREVS);
-    printf("|  %25s = %-7d |\n","MAX_ACTIONS",MAX_ACTIONS);
-    printf("|  %25s = %-7d |\n","MAX_ADJECTIVES",MAX_ADJECTIVES);
-    printf("|  %25s = %-7d |\n","ALLOC_CHUNK_SIZE",ALLOC_CHUNK_SIZE);
-    printf("|  %25s = %-7d |\n","MAX_ARRAYS",MAX_ARRAYS);
     printf("|  %25s = %-7d |\n","NUM_ATTR_BYTES",NUM_ATTR_BYTES);
-    printf("|  %25s = %-7d |\n","MAX_CLASSES",MAX_CLASSES);
-    printf("|  %25s = %-7d |\n","MAX_DICT_ENTRIES",MAX_DICT_ENTRIES);
     printf("|  %25s = %-7d |\n","DICT_WORD_SIZE",DICT_WORD_SIZE);
     if (glulx_mode)
       printf("|  %25s = %-7d |\n","DICT_CHAR_SIZE",DICT_CHAR_SIZE);
     printf("|  %25s = %-7d |\n","MAX_DYNAMIC_STRINGS",MAX_DYNAMIC_STRINGS);
-    printf("|  %25s = %-7d |\n","MAX_EXPRESSION_NODES",MAX_EXPRESSION_NODES);
-    printf("|  %25s = %-7d |\n","MAX_GLOBAL_VARIABLES",MAX_GLOBAL_VARIABLES);
     printf("|  %25s = %-7d |\n","HASH_TAB_SIZE",HASH_TAB_SIZE);
     if (!glulx_mode)
       printf("|  %25s = %-7d |\n","ZCODE_HEADER_EXT_WORDS",ZCODE_HEADER_EXT_WORDS);
     if (!glulx_mode)
       printf("|  %25s = %-7d |\n","ZCODE_HEADER_FLAGS_3",ZCODE_HEADER_FLAGS_3);
-    printf("|  %25s = %-7d |\n","MAX_INCLUSION_DEPTH",MAX_INCLUSION_DEPTH);
-    printf("|  %25s = %-7d |\n","MAX_INDIV_PROP_TABLE_SIZE", MAX_INDIV_PROP_TABLE_SIZE);
+    if (!glulx_mode)
+      printf("|  %25s = %-7d |\n","ZCODE_LESS_DICT_DATA",ZCODE_LESS_DICT_DATA);
     printf("|  %25s = %-7d |\n","INDIV_PROP_START", INDIV_PROP_START);
-    printf("|  %25s = %-7d |\n","MAX_LABELS",MAX_LABELS);
-    printf("|  %25s = %-7d |\n","MAX_LINESPACE",MAX_LINESPACE);
-    printf("|  %25s = %-7d |\n","MAX_LINK_DATA_SIZE",MAX_LINK_DATA_SIZE);
-    if (glulx_mode)
-      printf("|  %25s = %-7d |\n","MAX_LOCAL_VARIABLES",MAX_LOCAL_VARIABLES);
-    printf("|  %25s = %-7d |\n","MAX_LOW_STRINGS",MAX_LOW_STRINGS);
     if (glulx_mode)
       printf("|  %25s = %-7d |\n","MEMORY_MAP_EXTENSION",
         MEMORY_MAP_EXTENSION);
-    if (glulx_mode)
-      printf("|  %25s = %-7d |\n","MAX_NUM_STATIC_STRINGS",
-        MAX_NUM_STATIC_STRINGS);
-    printf("|  %25s = %-7d |\n","MAX_OBJECTS",MAX_OBJECTS);
     if (glulx_mode)
       printf("|  %25s = %-7d |\n","GLULX_OBJECT_EXT_BYTES",
         GLULX_OBJECT_EXT_BYTES);
-    if (glulx_mode)
-      printf("|  %25s = %-7d |\n","MAX_OBJ_PROP_COUNT",
-        MAX_OBJ_PROP_COUNT);
-    if (glulx_mode)
-      printf("|  %25s = %-7d |\n","MAX_OBJ_PROP_TABLE_SIZE",
-        MAX_OBJ_PROP_TABLE_SIZE);
-    printf("|  %25s = %-7d |\n","MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
-    printf("|  %25s = %-7d |\n","MAX_QTEXT_SIZE",MAX_QTEXT_SIZE);
-    printf("|  %25s = %-7d |\n","MAX_SOURCE_FILES",MAX_SOURCE_FILES);
     if (glulx_mode)
       printf("|  %25s = %-7ld |\n","MAX_STACK_SIZE",
            (long int) MAX_STACK_SIZE);
-    printf("|  %25s = %-7d |\n","MAX_STATIC_DATA",MAX_STATIC_DATA);
-    printf("|  %25s = %-7ld |\n","MAX_STATIC_STRINGS",
-           (long int) MAX_STATIC_STRINGS);
-    printf("|  %25s = %-7d |\n","MAX_SYMBOLS",MAX_SYMBOLS);
-    printf("|  %25s = %-7d |\n","SYMBOLS_CHUNK_SIZE",SYMBOLS_CHUNK_SIZE);
     printf("|  %25s = %-7d |\n","TRANSCRIPT_FORMAT",TRANSCRIPT_FORMAT);
-    printf("|  %25s = %-7ld |\n","MAX_TRANSCRIPT_SIZE",
-           (long int) MAX_TRANSCRIPT_SIZE);
-    if (glulx_mode)
-      printf("|  %25s = %-7ld |\n","MAX_UNICODE_CHARS",
-           (long int) MAX_UNICODE_CHARS);
     printf("|  %25s = %-7d |\n","WARN_UNUSED_ROUTINES",WARN_UNUSED_ROUTINES);
     printf("|  %25s = %-7d |\n","OMIT_UNUSED_ROUTINES",OMIT_UNUSED_ROUTINES);
-    printf("|  %25s = %-7d |\n","MAX_VERBS",MAX_VERBS);
-    printf("|  %25s = %-7d |\n","MAX_VERBSPACE",MAX_VERBSPACE);
-    printf("|  %25s = %-7ld |\n","MAX_ZCODE_SIZE",
-           (long int) MAX_ZCODE_SIZE);
+    printf("|  %25s = %-7d |\n","STRIP_UNREACHABLE_LABELS",STRIP_UNREACHABLE_LABELS);
     printf("+--------------------------------------+\n");
 }
 
-extern void set_memory_sizes(int size_flag)
+extern void set_memory_sizes(void)
 {
-    if (size_flag == HUGE_SIZE)
-    {
-        MAX_QTEXT_SIZE  = 4000;
-        MAX_SYMBOLS     = 10000;
-
-        SYMBOLS_CHUNK_SIZE = 5000;
-        HASH_TAB_SIZE      = 512;
-
-        MAX_OBJECTS = 640;
-
-        MAX_ACTIONS      = 200;
-        MAX_ADJECTIVES   = 50;
-        MAX_DICT_ENTRIES = 2000;
-        MAX_STATIC_DATA  = 10000;
-
-        MAX_PROP_TABLE_SIZE_z = 30000;
-        MAX_PROP_TABLE_SIZE_g = 60000;
-
-        MAX_EXPRESSION_NODES = 100;
-        MAX_VERBS = 200;
-        MAX_VERBSPACE = 4096;
-        MAX_LABELS = 1000;
-        MAX_LINESPACE = 16000;
-
-        MAX_STATIC_STRINGS = 8000;
-        MAX_ZCODE_SIZE_z = 20000;
-        MAX_ZCODE_SIZE_g = 40000;
-        MAX_LINK_DATA_SIZE = 2000;
-
-        MAX_LOW_STRINGS = 2048;
-
-        MAX_TRANSCRIPT_SIZE = 200000;
-        MAX_NUM_STATIC_STRINGS = 20000;
-
-        MAX_CLASSES = 64;
-
-        MAX_OBJ_PROP_COUNT = 128;
-        MAX_OBJ_PROP_TABLE_SIZE = 4096;
-
-        MAX_INDIV_PROP_TABLE_SIZE = 15000;
-        MAX_ARRAYS = 128;
-
-        MAX_GLOBAL_VARIABLES_z = 240;
-        MAX_GLOBAL_VARIABLES_g = 512;
-        
-        ALLOC_CHUNK_SIZE_z = 8192;
-        ALLOC_CHUNK_SIZE_g = 32768;
-    }
-    if (size_flag == LARGE_SIZE)
-    {
-        MAX_QTEXT_SIZE  = 4000;
-        MAX_SYMBOLS     = 6400;
-
-        SYMBOLS_CHUNK_SIZE = 5000;
-        HASH_TAB_SIZE      = 512;
-
-        MAX_OBJECTS = 512;
-
-        MAX_ACTIONS      = 200;
-        MAX_ADJECTIVES   = 50;
-        MAX_DICT_ENTRIES = 1300;
-        MAX_STATIC_DATA  = 10000;
-
-        MAX_PROP_TABLE_SIZE_z = 15000;
-        MAX_PROP_TABLE_SIZE_g = 30000;
-
-        MAX_EXPRESSION_NODES = 100;
-        MAX_VERBS = 140;
-        MAX_VERBSPACE = 4096;
-        MAX_LINESPACE = 10000;
-
-        MAX_LABELS = 1000;
-        MAX_STATIC_STRINGS = 8000;
-        MAX_ZCODE_SIZE_z = 20000;
-        MAX_ZCODE_SIZE_g = 40000;
-        MAX_LINK_DATA_SIZE = 2000;
-
-        MAX_LOW_STRINGS = 2048;
-
-        MAX_TRANSCRIPT_SIZE = 200000;
-        MAX_NUM_STATIC_STRINGS = 20000;
-
-        MAX_CLASSES = 64;
-
-        MAX_OBJ_PROP_COUNT = 64;
-        MAX_OBJ_PROP_TABLE_SIZE = 2048;
-
-        MAX_INDIV_PROP_TABLE_SIZE = 10000;
-        MAX_ARRAYS = 128;
-
-        MAX_GLOBAL_VARIABLES_z = 240;
-        MAX_GLOBAL_VARIABLES_g = 512;
-        
-        ALLOC_CHUNK_SIZE_z = 8192;
-        ALLOC_CHUNK_SIZE_g = 16384;
-    }
-    if (size_flag == SMALL_SIZE)
-    {
-        MAX_QTEXT_SIZE  = 4000;
-        MAX_SYMBOLS     = 3000;
-
-        SYMBOLS_CHUNK_SIZE = 2500;
-        HASH_TAB_SIZE      = 512;
-
-        MAX_OBJECTS = 300;
-
-        MAX_ACTIONS      = 200;
-        MAX_ADJECTIVES   = 50;
-        MAX_DICT_ENTRIES = 700;
-        MAX_STATIC_DATA  = 10000;
-
-        MAX_PROP_TABLE_SIZE_z = 8000;
-        MAX_PROP_TABLE_SIZE_g = 16000;
-
-        MAX_EXPRESSION_NODES = 40;
-        MAX_VERBS = 110;
-        MAX_VERBSPACE = 2048;
-        MAX_LINESPACE = 10000;
-        MAX_LABELS = 1000;
-
-        MAX_STATIC_STRINGS = 8000;
-        MAX_ZCODE_SIZE_z = 10000;
-        MAX_ZCODE_SIZE_g = 20000;
-        MAX_LINK_DATA_SIZE = 1000;
-
-        MAX_LOW_STRINGS = 1024;
-
-        MAX_TRANSCRIPT_SIZE = 100000;
-        MAX_NUM_STATIC_STRINGS = 10000;
-
-        MAX_CLASSES = 32;
-
-        MAX_OBJ_PROP_COUNT = 64;
-        MAX_OBJ_PROP_TABLE_SIZE = 1024;
-
-        MAX_INDIV_PROP_TABLE_SIZE = 5000;
-        MAX_ARRAYS = 64;
-
-        MAX_GLOBAL_VARIABLES_z = 240;
-        MAX_GLOBAL_VARIABLES_g = 256;
-        
-        ALLOC_CHUNK_SIZE_z = 8192;
-        ALLOC_CHUNK_SIZE_g = 8192;
-    }
-
-    /* Regardless of size_flag... */
-    MAX_SOURCE_FILES = 256;
-    MAX_INCLUSION_DEPTH = 5;
-    MAX_LOCAL_VARIABLES_z = 16;
-    MAX_LOCAL_VARIABLES_g = 32;
+    HASH_TAB_SIZE      = 512;
     DICT_CHAR_SIZE = 1;
     DICT_WORD_SIZE_z = 6;
     DICT_WORD_SIZE_g = 9;
@@ -540,14 +342,14 @@ extern void set_memory_sizes(int size_flag)
     NUM_ATTR_BYTES_g = 7;
     MAX_ABBREVS = 64;
     MAX_DYNAMIC_STRINGS_z = 32;
-    MAX_DYNAMIC_STRINGS_g = 64;
+    MAX_DYNAMIC_STRINGS_g = 100;
     /* Backwards-compatible behavior: allow for a unicode table
        whether we need one or not. The user can set this to zero if
        there's no unicode table. */
     ZCODE_HEADER_EXT_WORDS = 3;
     ZCODE_HEADER_FLAGS_3 = 0;
+    ZCODE_LESS_DICT_DATA = 0;
     GLULX_OBJECT_EXT_BYTES = 0;
-    MAX_UNICODE_CHARS = 64;
     MEMORY_MAP_EXTENSION = 0;
     /* We estimate the default Glulx stack size at 4096. That's about
        enough for 90 nested function calls with 8 locals each -- the
@@ -557,6 +359,7 @@ extern void set_memory_sizes(int size_flag)
     MAX_STACK_SIZE = 4096;
     OMIT_UNUSED_ROUTINES = 0;
     WARN_UNUSED_ROUTINES = 0;
+    STRIP_UNREACHABLE_LABELS = 1;
     TRANSCRIPT_FORMAT = 0;
 
     adjust_memory_sizes();
@@ -565,24 +368,14 @@ extern void set_memory_sizes(int size_flag)
 extern void adjust_memory_sizes()
 {
   if (!glulx_mode) {
-    MAX_ZCODE_SIZE = MAX_ZCODE_SIZE_z;
-    MAX_PROP_TABLE_SIZE = MAX_PROP_TABLE_SIZE_z;
-    MAX_GLOBAL_VARIABLES = MAX_GLOBAL_VARIABLES_z;
-    MAX_LOCAL_VARIABLES = MAX_LOCAL_VARIABLES_z;
     DICT_WORD_SIZE = DICT_WORD_SIZE_z;
     NUM_ATTR_BYTES = NUM_ATTR_BYTES_z;
-    ALLOC_CHUNK_SIZE = ALLOC_CHUNK_SIZE_z;
     MAX_DYNAMIC_STRINGS = MAX_DYNAMIC_STRINGS_z;
     INDIV_PROP_START = 64;
   }
   else {
-    MAX_ZCODE_SIZE = MAX_ZCODE_SIZE_g;
-    MAX_PROP_TABLE_SIZE = MAX_PROP_TABLE_SIZE_g;
-    MAX_GLOBAL_VARIABLES = MAX_GLOBAL_VARIABLES_g;
-    MAX_LOCAL_VARIABLES = MAX_LOCAL_VARIABLES_g;
     DICT_WORD_SIZE = DICT_WORD_SIZE_g;
     NUM_ATTR_BYTES = NUM_ATTR_BYTES_g;
-    ALLOC_CHUNK_SIZE = ALLOC_CHUNK_SIZE_g;
     MAX_DYNAMIC_STRINGS = MAX_DYNAMIC_STRINGS_g;
     INDIV_PROP_START = 256;
   }
@@ -590,56 +383,12 @@ extern void adjust_memory_sizes()
 
 static void explain_parameter(char *command)
 {   printf("\n");
-    if (strcmp(command,"MAX_QTEXT_SIZE")==0)
-    {   printf(
-"  MAX_QTEXT_SIZE is the maximum length of a quoted string.  Increasing\n\
-   by 1 costs 5 bytes (for lexical analysis memory).  Inform automatically\n\
-   ensures that MAX_STATIC_STRINGS is at least twice the size of this.");
-        return;
-    }
-    if (strcmp(command,"MAX_SYMBOLS")==0)
-    {   printf(
-"  MAX_SYMBOLS is the maximum number of symbols - names of variables, \n\
-  objects, routines, the many internal Inform-generated names and so on.\n");
-        return;
-    }
-    if (strcmp(command,"SYMBOLS_CHUNK_SIZE")==0)
-    {   printf(
-"  The symbols names are stored in memory which is allocated in chunks \n\
-  of size SYMBOLS_CHUNK_SIZE.\n");
-        return;
-    }
     if (strcmp(command,"HASH_TAB_SIZE")==0)
     {   printf(
 "  HASH_TAB_SIZE is the size of the hash tables used for the heaviest \n\
   symbols banks.\n");
         return;
     }
-    if (strcmp(command,"MAX_OBJECTS")==0)
-    {   printf(
-"  MAX_OBJECTS is the maximum number of objects.  (If compiling a version-3 \n\
-  game, 255 is an absolute maximum in any event.)\n");
-        return;
-    }
-    if (strcmp(command,"MAX_ACTIONS")==0)
-    {   printf(
-"  MAX_ACTIONS is the maximum number of actions - that is, routines such as \n\
-  TakeSub which are referenced in the grammar table.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_ADJECTIVES")==0)
-    {   printf(
-"  MAX_ADJECTIVES is the maximum number of different \"adjectives\" in the \n\
-  grammar table.  Adjectives are misleadingly named: they are words such as \n\
-  \"in\", \"under\" and the like.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_DICT_ENTRIES")==0)
-    {   printf(
-"  MAX_DICT_ENTRIES is the maximum number of words which can be entered \n\
-  into the game's dictionary.  It costs 29 bytes to increase this by one.\n");
-        return;
-    }
     if (strcmp(command,"DICT_WORD_SIZE")==0)
     {   printf(
 "  DICT_WORD_SIZE is the number of characters in a dictionary word. In \n\
@@ -677,6 +426,12 @@ static void explain_parameter(char *command)
   header extension table (Z-Spec 1.1).\n");
         return;
     }
+    if (strcmp(command,"ZCODE_LESS_DICT_DATA")==0)
+    {   printf(
+"  ZCODE_LESS_DICT_DATA, if set, provides each dict word with two data bytes\n\
+  rather than three. (Z-code only.)\n");
+        return;
+    }
     if (strcmp(command,"GLULX_OBJECT_EXT_BYTES")==0)
     {   printf(
 "  GLULX_OBJECT_EXT_BYTES is an amount of additional space to add to each \n\
@@ -685,134 +440,17 @@ static void explain_parameter(char *command)
   specifies the object structure.)\n");
         return;
     }
-    if (strcmp(command,"MAX_STATIC_DATA")==0)
-    {   printf(
-"  MAX_STATIC_DATA is the size of an array of integers holding initial \n\
-  values for arrays and strings stored as ASCII inside the Z-machine.  It \n\
-  should be at least 1024 but seldom needs much more.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_PROP_TABLE_SIZE")==0)
-    {   printf(
-"  MAX_PROP_TABLE_SIZE is the number of bytes allocated to hold the \n\
-  properties table.\n");
-        return;
-    }
     if (strcmp(command,"MAX_ABBREVS")==0)
     {   printf(
 "  MAX_ABBREVS is the maximum number of declared abbreviations.  It is not \n\
-  allowed to exceed 96 in Z-code.\n");
+  allowed to exceed 96 in Z-code. (This is not meaningful in Glulx, where \n\
+  there is no limit on abbreviations.)\n");
         return;
     }
     if (strcmp(command,"MAX_DYNAMIC_STRINGS")==0)
     {   printf(
 "  MAX_DYNAMIC_STRINGS is the maximum number of string substitution variables\n\
-  (\"@00\").  It is not allowed to exceed 96 in Z-code or 100 in Glulx.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_ARRAYS")==0)
-    {   printf(
-"  MAX_ARRAYS is the maximum number of declared arrays.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_EXPRESSION_NODES")==0)
-    {   printf(
-"  MAX_EXPRESSION_NODES is the maximum number of nodes in the expression \n\
-  evaluator's storage for parse trees.  In effect, it measures how \n\
-  complicated algebraic expressions are allowed to be.  Increasing it by \n\
-  one costs about 80 bytes.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_VERBS")==0)
-    {   printf(
-"  MAX_VERBS is the maximum number of verbs (such as \"take\") which can be \n\
-  defined, each with its own grammar.  To increase it by one costs about\n\
-  128 bytes.  A full game will contain at least 100.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_VERBSPACE")==0)
-    {   printf(
-"  MAX_VERBSPACE is the size of workspace used to store verb words, so may\n\
-  need increasing in games with many synonyms: unlikely to exceed 4K.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_LABELS")==0)
-    {   printf(
-"  MAX_LABELS is the maximum number of label points in any one routine.\n\
-  (If the -k debugging information switch is set, MAX_LABELS is raised to\n\
-  a minimum level of 2000, as about twice the normal number of label points\n\
-  are needed to generate tables of how source code corresponds to positions\n\
-  in compiled code.)");
-        return;
-    }
-    if (strcmp(command,"MAX_LINESPACE")==0)
-    {   printf(
-"  MAX_LINESPACE is the size of workspace used to store grammar lines, so \n\
-  may need increasing in games with complex or extensive grammars.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_STATIC_STRINGS")==0)
-    {
-        printf(
-"  MAX_STATIC_STRINGS is the size in bytes of a buffer to hold compiled\n\
-  strings before they're written into longer-term storage.  2000 bytes is \n\
-  plenty, allowing string constants of up to about 3000 characters long.\n\
-  Inform automatically ensures that this is at least twice the size of\n\
-  MAX_QTEXT_SIZE, to be on the safe side.");
-        return;
-    }
-    if (strcmp(command,"MAX_ZCODE_SIZE")==0)
-    {
-        printf(
-"  MAX_ZCODE_SIZE is the size in bytes of a buffer to hold compiled \n\
-  code for a single routine.  (It applies to both Z-code and Glulx, \n\
-  despite the name.)  As a guide, the longest library routine is \n\
-  about 6500 bytes long in Z-code; about twice that in Glulx.");
-        return;
-    }
-    if (strcmp(command,"MAX_LINK_DATA_SIZE")==0)
-    {
-        printf(
-"  MAX_LINK_DATA_SIZE is the size in bytes of a buffer to hold module \n\
-  link data before it's written into longer-term storage.  2000 bytes \n\
-  is plenty.");
-        return;
-    }
-    if (strcmp(command,"MAX_LOW_STRINGS")==0)
-    {   printf(
-"  MAX_LOW_STRINGS is the size in bytes of a buffer to hold all the \n\
-  compiled \"low strings\" which are to be written above the synonyms table \n\
-  in the Z-machine.  1024 is plenty.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_TRANSCRIPT_SIZE")==0)
-    {   printf(
-"  MAX_TRANSCRIPT_SIZE is only allocated for the abbreviations optimisation \n\
-  switch, and has the size in bytes of a buffer to hold the entire text of\n\
-  the game being compiled: it has to be enormous, say 100000 to 200000.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_CLASSES")==0)
-    {   printf(
-"  MAX_CLASSES maximum number of object classes which can be defined.  This\n\
-  is cheap to increase.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_INCLUSION_DEPTH")==0)
-    {   printf(
-"  MAX_INCLUSION_DEPTH is the number of nested includes permitted.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_SOURCE_FILES")==0)
-    {   printf(
-"  MAX_SOURCE_FILES is the number of source files that can be read in the \n\
-  compilation.\n");
-        return;
-    }
-    if (strcmp(command,"MAX_INDIV_PROP_TABLE_SIZE")==0)
-    {   printf(
-"  MAX_INDIV_PROP_TABLE_SIZE is the number of bytes allocated to hold the \n\
-  table of ..variable values.\n");
+  (\"@00\" or \"@(0)\").  It is not allowed to exceed 96 in Z-code.\n");
         return;
     }
     if (strcmp(command,"INDIV_PROP_START")==0)
@@ -821,52 +459,6 @@ static void explain_parameter(char *command)
   properties are numbered INDIV_PROP_START and up.\n");
         return;
     }
-    if (strcmp(command,"MAX_OBJ_PROP_COUNT")==0)
-    {   printf(
-"  MAX_OBJ_PROP_COUNT is the maximum number of properties a single object \n\
-  can have. (Glulx only)\n");
-        return;
-    }
-    if (strcmp(command,"MAX_OBJ_PROP_TABLE_SIZE")==0)
-    {   printf(
-"  MAX_OBJ_PROP_TABLE_SIZE is the number of words allocated to hold a \n\
-  single object's properties. (Glulx only)\n");
-        return;
-    }
-    if (strcmp(command,"MAX_LOCAL_VARIABLES")==0)
-    {   printf(
-"  MAX_LOCAL_VARIABLES is the number of local variables (including \n\
-  arguments) allowed in a procedure. (Glulx only)\n");
-        return;
-    }
-    if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0)
-    {   printf(
-"  MAX_GLOBAL_VARIABLES is the number of global variables allowed in the \n\
-  program. (Glulx only)\n");
-        return;
-    }
-    if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0)
-    {
-        printf(
-"  MAX_NUM_STATIC_STRINGS is the maximum number of compiled strings \n\
-  allowed in the program. (Glulx only)\n");
-        return;
-    }
-    if (strcmp(command,"MAX_UNICODE_CHARS")==0)
-    {
-        printf(
-"  MAX_UNICODE_CHARS is the maximum number of different Unicode characters \n\
-  (beyond the Latin-1 range, $00..$FF) which the game text can use. \n\
-  (Glulx only)\n");
-        return;
-    }
-    if (strcmp(command,"ALLOC_CHUNK_SIZE")==0)
-    {
-        printf(
-"  ALLOC_CHUNK_SIZE is a base unit of Inform's internal memory allocation \n\
-  for various structures.\n");
-        return;
-    }
     if (strcmp(command,"MAX_STACK_SIZE")==0)
     {
         printf(
@@ -904,6 +496,14 @@ static void explain_parameter(char *command)
   into the game file.\n");
         return;
     }
+    if (strcmp(command,"STRIP_UNREACHABLE_LABELS")==0)
+    {
+        printf(
+"  STRIP_UNREACHABLE_LABELS, if set to 1, will skip labels in unreachable \n\
+  statements. Jumping to a skipped label is an error. If 0, all labels \n\
+  will be compiled, at the cost of less optimized code. The default is 1.\n");
+        return;
+    }
     if (strcmp(command,"SERIAL")==0)
     {
         printf(
@@ -1002,6 +602,136 @@ static void add_predefined_symbol(char *command)
     add_config_symbol_definition(command, value);
 }
 
+static void set_trace_option(char *command)
+{
+    char *cx;
+    int value;
+
+    /* Parse options of the form STRING or STRING=NUM. (The $! has already been eaten.) If the string is null or empty, show help. */
+    
+    if (!command || *command == '\0') {
+        printf("The full list of trace options:\n\n");
+        printf("  ACTIONS: show actions defined\n");
+        printf("  ASM: trace assembly (same as -a)\n");
+        printf("    ASM=2: also show hex dumps\n");
+        printf("    ASM=3: also show branch optimization info\n");
+        printf("    ASM=4: more verbose branch info\n");
+        printf("  BPATCH: show backpatch results\n");
+        printf("    BPATCH=2: also show markers added\n");
+        printf("  DICT: display the dictionary table\n");
+        printf("    DICT=2: also the byte encoding of entries\n");
+        printf("  EXPR: show expression trees\n");
+        printf("    EXPR=2: more verbose\n");
+        printf("    EXPR=3: even more verbose\n");
+        printf("  FILES: show files opened\n");
+        printf("  FINDABBREVS: show selection decisions during abbreviation optimization\n    (only meaningful with -u)\n");
+        printf("    FINDABBREVS=2: also show three-letter-block decisions\n");
+        printf("  FREQ: show how efficient abbreviations were (same as -f)\n    (only meaningful with -e)\n");
+        printf("  LINKER: show module linking info\n");
+        printf("    LINKER=2: more verbose (or 3, 4 for even more)\n");
+        printf("  MAP: print memory map of the virtual machine (same as -z)\n");
+        printf("    MAP=2: also show percentage of VM that each segment occupies\n");
+        printf("  MEM: show internal memory allocations\n");
+        printf("  OBJECTS: display the object table\n");
+        printf("  PROPS: show attributes and properties defined\n");
+        printf("  RUNTIME: show game function calls at runtime (same as -g)\n");
+        printf("    RUNTIME=2: also show library calls (not supported in Glulx)\n");
+        printf("    RUNTIME=3: also show veneer calls (not supported in Glulx)\n");
+        printf("  STATS: give compilation statistics (same as -s)\n");
+        printf("  SYMBOLS: display the symbol table\n");
+        printf("    SYMBOLS=2: also show compiler-defined symbols\n");
+        printf("  SYMDEF: show when symbols are noticed and defined\n");
+        printf("  TOKENS: show token lexing\n");
+        printf("    TOKENS=2: also show token types\n");
+        printf("    TOKENS=3: also show lexical context\n");
+        printf("  VERBS: display the verb grammar table\n");
+        return;
+    }
+
+    for (cx=command; *cx && *cx != '='; cx++) {
+        if (!(*cx >= 'A' && *cx <= 'Z')) {
+            printf("Invalid $! trace command \"%s\"\n", command);
+            return;
+        }
+    }
+
+    value = 1;
+    if (*cx == '=') {
+        char *ex;
+        value = strtol(cx+1, &ex, 10);
+        
+        if (ex == cx+1 || *ex != '\0' || value < 0) {
+            printf("Bad numerical setting in $! trace command \"%s\"\n", command);
+            return;
+        }
+        
+        *cx = '\0';
+    }
+
+    /* We accept some reasonable synonyms, including plausible singular/plural confusion. */
+    
+    if (strcmp(command, "ASSEMBLY")==0 || strcmp(command, "ASM")==0) {
+        asm_trace_setting = value;
+    }
+    else if (strcmp(command, "ACTION")==0 || strcmp(command, "ACTIONS")==0) {
+        printactions_switch = value;
+    }
+    else if (strcmp(command, "BPATCH")==0 || strcmp(command, "BACKPATCH")==0) {
+        bpatch_trace_setting = value;
+    }
+    else if (strcmp(command, "DICTIONARY")==0 || strcmp(command, "DICT")==0) {
+        list_dict_setting = value;
+    }
+    else if (strcmp(command, "EXPR")==0 || strcmp(command, "EXPRESSION")==0 || strcmp(command, "EXPRESSIONS")==0) {
+        expr_trace_setting = value;
+    }
+    else if (strcmp(command, "FILE")==0 || strcmp(command, "FILES")==0) {
+        files_trace_setting = value;
+    }
+    else if (strcmp(command, "FINDABBREV")==0 || strcmp(command, "FINDABBREVS")==0) {
+        optabbrevs_trace_setting = value;
+    }
+    else if (strcmp(command, "FREQUENCY")==0 || strcmp(command, "FREQUENCIES")==0 || strcmp(command, "FREQ")==0) {
+        frequencies_setting = value;
+    }
+    else if (strcmp(command, "LINK")==0 || strcmp(command, "LINKER")==0) {
+        linker_trace_setting = value;
+    }
+    else if (strcmp(command, "MAP")==0) {
+        memory_map_setting = value;
+    }
+    else if (strcmp(command, "MEM")==0 || strcmp(command, "MEMORY")==0) {
+        memout_switch = value;
+    }
+    else if (strcmp(command, "OBJECTS")==0 || strcmp(command, "OBJECT")==0 || strcmp(command, "OBJS")==0 || strcmp(command, "OBJ")==0) {
+        list_objects_setting = value;
+    }
+    else if (strcmp(command, "PROP")==0 || strcmp(command, "PROPERTY")==0 || strcmp(command, "PROPS")==0 || strcmp(command, "PROPERTIES")==0) {
+        printprops_switch = value;
+    }
+    else if (strcmp(command, "RUNTIME")==0) {
+        trace_fns_setting = value;
+    }
+    else if (strcmp(command, "STATISTICS")==0 || strcmp(command, "STATS")==0 || strcmp(command, "STAT")==0) {
+        statistics_switch = value;
+    }
+    else if (strcmp(command, "SYMBOLS")==0 || strcmp(command, "SYMBOL")==0) {
+        list_symbols_setting = value;
+    }
+    else if (strcmp(command, "SYMDEF")==0 || strcmp(command, "SYMBOLDEF")==0) {
+        symdef_trace_setting = value;
+    }
+    else if (strcmp(command, "TOKEN")==0 || strcmp(command, "TOKENS")==0) {
+        tokens_trace_setting = value;
+    }
+    else if (strcmp(command, "VERBS")==0 || strcmp(command, "VERB")==0) {
+        list_verbs_setting = value;
+    }
+    else {
+        printf("Unrecognized $! trace command \"%s\"\n", command);
+    }
+}
+
 /* Handle a dollar-sign command option: $LIST, $FOO=VAL, and so on.
    The option may come from the command line, an ICL file, or a header
    comment.
@@ -1020,11 +750,18 @@ extern void memory_command(char *command)
 
     if (command[0]=='?') { explain_parameter(command+1); return; }
     if (command[0]=='#') { add_predefined_symbol(command+1); return; }
+    if (command[0]=='!') { set_trace_option(command+1); return; }
 
-    if (strcmp(command, "HUGE")==0) { set_memory_sizes(HUGE_SIZE); return; }
-    if (strcmp(command, "LARGE")==0) { set_memory_sizes(LARGE_SIZE); return; }
-    if (strcmp(command, "SMALL")==0) { set_memory_sizes(SMALL_SIZE); return; }
+    if (strcmp(command, "HUGE")==0
+        || strcmp(command, "LARGE")==0
+        || strcmp(command, "SMALL")==0) {
+        if (!nowarnings_switch)
+            printf("The Inform 6 memory size commands (\"SMALL, LARGE, HUGE\") are no longer needed and has been withdrawn.\n");
+        return;
+    }
+    
     if (strcmp(command, "LIST")==0)  { list_memory_sizes(); return; }
+    
     for (i=0; command[i]!=0; i++)
     {   if (command[i]=='=')
         {   command[i]=0;
@@ -1034,28 +771,25 @@ extern void memory_command(char *command)
             if (strcmp(command,"BUFFER_LENGTH")==0)
                 flag=2;
             if (strcmp(command,"MAX_QTEXT_SIZE")==0)
-            {   MAX_QTEXT_SIZE=j, flag=1;
-                if (2*MAX_QTEXT_SIZE > MAX_STATIC_STRINGS)
-                    MAX_STATIC_STRINGS = 2*MAX_QTEXT_SIZE;
-            }
+                flag=3;
             if (strcmp(command,"MAX_SYMBOLS")==0)
-                MAX_SYMBOLS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_BANK_SIZE")==0)
                 flag=2;
             if (strcmp(command,"SYMBOLS_CHUNK_SIZE")==0)
-                SYMBOLS_CHUNK_SIZE=j, flag=1;
+                flag=3;
             if (strcmp(command,"BANK_CHUNK_SIZE")==0)
                 flag=2;
             if (strcmp(command,"HASH_TAB_SIZE")==0)
                 HASH_TAB_SIZE=j, flag=1;
             if (strcmp(command,"MAX_OBJECTS")==0)
-                MAX_OBJECTS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_ACTIONS")==0)
-                MAX_ACTIONS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_ADJECTIVES")==0)
-                MAX_ADJECTIVES=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_DICT_ENTRIES")==0)
-                MAX_DICT_ENTRIES=j, flag=1;
+                flag=3;
             if (strcmp(command,"DICT_WORD_SIZE")==0) 
             {   DICT_WORD_SIZE=j, flag=1;
                 DICT_WORD_SIZE_g=DICT_WORD_SIZE_z=j;
@@ -1070,10 +804,12 @@ extern void memory_command(char *command)
                 ZCODE_HEADER_EXT_WORDS=j, flag=1;
             if (strcmp(command,"ZCODE_HEADER_FLAGS_3")==0)
                 ZCODE_HEADER_FLAGS_3=j, flag=1;
+            if (strcmp(command,"ZCODE_LESS_DICT_DATA")==0)
+                ZCODE_LESS_DICT_DATA=j, flag=1;
             if (strcmp(command,"GLULX_OBJECT_EXT_BYTES")==0)
                 GLULX_OBJECT_EXT_BYTES=j, flag=1;
             if (strcmp(command,"MAX_STATIC_DATA")==0)
-                MAX_STATIC_DATA=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_OLDEPTH")==0)
                 flag=2;
             if (strcmp(command,"MAX_ROUTINES")==0)
@@ -1081,9 +817,7 @@ extern void memory_command(char *command)
             if (strcmp(command,"MAX_GCONSTANTS")==0)
                 flag=2;
             if (strcmp(command,"MAX_PROP_TABLE_SIZE")==0)
-            {   MAX_PROP_TABLE_SIZE=j, flag=1;
-                MAX_PROP_TABLE_SIZE_g=MAX_PROP_TABLE_SIZE_z=j;
-            }
+                flag=3;
             if (strcmp(command,"MAX_FORWARD_REFS")==0)
                 flag=2;
             if (strcmp(command,"STACK_SIZE")==0)
@@ -1099,62 +833,51 @@ extern void memory_command(char *command)
                 MAX_DYNAMIC_STRINGS_g=MAX_DYNAMIC_STRINGS_z=j;
             }
             if (strcmp(command,"MAX_ARRAYS")==0)
-                MAX_ARRAYS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_EXPRESSION_NODES")==0)
-                MAX_EXPRESSION_NODES=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_VERBS")==0)
-                MAX_VERBS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_VERBSPACE")==0)
-                MAX_VERBSPACE=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_LABELS")==0)
-                MAX_LABELS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_LINESPACE")==0)
-                MAX_LINESPACE=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0)
-                MAX_NUM_STATIC_STRINGS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_STATIC_STRINGS")==0)
-            {   MAX_STATIC_STRINGS=j, flag=1;
-                if (2*MAX_QTEXT_SIZE > MAX_STATIC_STRINGS)
-                    MAX_STATIC_STRINGS = 2*MAX_QTEXT_SIZE;
-            }
+                flag=3;
             if (strcmp(command,"MAX_ZCODE_SIZE")==0)
-            {   MAX_ZCODE_SIZE=j, flag=1;
-                MAX_ZCODE_SIZE_g=MAX_ZCODE_SIZE_z=j;
-            }
+                flag=3;
             if (strcmp(command,"MAX_LINK_DATA_SIZE")==0)
-                MAX_LINK_DATA_SIZE=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_LOW_STRINGS")==0)
-                MAX_LOW_STRINGS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_TRANSCRIPT_SIZE")==0)
-                MAX_TRANSCRIPT_SIZE=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_CLASSES")==0)
-                MAX_CLASSES=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_INCLUSION_DEPTH")==0)
-                MAX_INCLUSION_DEPTH=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_SOURCE_FILES")==0)
-                MAX_SOURCE_FILES=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_INDIV_PROP_TABLE_SIZE")==0)
-                MAX_INDIV_PROP_TABLE_SIZE=j, flag=1;
+                flag=3;
             if (strcmp(command,"INDIV_PROP_START")==0)
                 INDIV_PROP_START=j, flag=1;
             if (strcmp(command,"MAX_OBJ_PROP_TABLE_SIZE")==0)
-                MAX_OBJ_PROP_TABLE_SIZE=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_OBJ_PROP_COUNT")==0)
-                MAX_OBJ_PROP_COUNT=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_LOCAL_VARIABLES")==0)
-            {   MAX_LOCAL_VARIABLES=j, flag=1;
-                MAX_LOCAL_VARIABLES_g=MAX_LOCAL_VARIABLES_z=j;
-            }
+                flag=3;
             if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0)
-            {   MAX_GLOBAL_VARIABLES=j, flag=1;
-                MAX_GLOBAL_VARIABLES_g=MAX_GLOBAL_VARIABLES_z=j;
-            }
+                flag=3;
             if (strcmp(command,"ALLOC_CHUNK_SIZE")==0)
-            {   ALLOC_CHUNK_SIZE=j, flag=1;
-                ALLOC_CHUNK_SIZE_g=ALLOC_CHUNK_SIZE_z=j;
-            }
+                flag=3;
             if (strcmp(command,"MAX_UNICODE_CHARS")==0)
-                MAX_UNICODE_CHARS=j, flag=1;
+                flag=3;
             if (strcmp(command,"MAX_STACK_SIZE")==0)
             {
                 MAX_STACK_SIZE=j, flag=1;
@@ -1185,6 +908,12 @@ extern void memory_command(char *command)
                 if (OMIT_UNUSED_ROUTINES > 1 || OMIT_UNUSED_ROUTINES < 0)
                     OMIT_UNUSED_ROUTINES = 1;
             }
+            if (strcmp(command,"STRIP_UNREACHABLE_LABELS")==0)
+            {
+                STRIP_UNREACHABLE_LABELS=j, flag=1;
+                if (STRIP_UNREACHABLE_LABELS > 1 || STRIP_UNREACHABLE_LABELS < 0)
+                    STRIP_UNREACHABLE_LABELS = 1;
+            }
             if (strcmp(command,"SERIAL")==0)
             {
                 if (j >= 0 && j <= 999999)
@@ -1197,9 +926,10 @@ extern void memory_command(char *command)
 
             if (flag==0)
                 printf("No such memory setting as \"%s\"\n", command);
-            if (flag==2)
-            printf("The Inform 5 memory setting \"%s\" has been withdrawn.\n\
-It should be safe to omit it (putting nothing in its place).\n", command);
+            if (flag==2 && !nowarnings_switch)
+                printf("The Inform 5 memory setting \"%s\" has been withdrawn.\n", command);
+            if (flag==3 && !nowarnings_switch)
+                printf("The Inform 6 memory setting \"%s\" is no longer needed and has been withdrawn.\n", command);
             return;
         }
     }
index b957073d941f73c109ecf8b760bdb9d0d8dd0c54..34e0b8673d5807ef701b5d5c180afc85316002b3 100644 (file)
@@ -6,8 +6,8 @@
 /*                    checks syntax and translates such directives into      */
 /*                    specifications for the object-maker.                   */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -20,7 +20,7 @@
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -47,30 +47,34 @@ static fpropt full_object;             /* "fpropt" is a typedef for a struct
                                           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                     */
+                                          are allocated dynamically as
+                                          memory-lists                       */
+
 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                      */
+static memory_list current_object_name; /* The name of the object currently
+                                           being defined.                    */
+
+static int current_classname_symbol;    /* The symbol index of the class
+                                           currently being defined.
+                                           For error-checking and printing
+                                           names of embedded routines only.  */
+
+static memory_list embedded_function_name; /* Temporary storage for inline
+                                              function name in property.     */
 
 /* ------------------------------------------------------------------------- */
 /*   Classes.                                                                */
 /* ------------------------------------------------------------------------- */
 /*   Arrays defined below:                                                   */
 /*                                                                           */
-/*    int32 class_begins_at[n]            offset of properties block for     */
-/*                                        nth class (always an offset        */
-/*                                        inside the properties_table)       */
+/*    classinfo class_info[]              Object number and prop offset      */
 /*    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  */
@@ -92,13 +96,19 @@ int no_attributes,                 /* Number of attributes defined so far    */
                                       others itself, so the variable begins
                                       the compilation pass set to 4)         */
 
+/* Print a PROPS trace line. The f flag is 0 for an attribute, 1 for
+   a common property, 2 for an individual property. */
 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);
+    char *stype = "";
+    if (f == 0) stype = "Attr";
+    else if (f == 1) stype = "Prop";
+    else if (f == 2) stype = "Indiv";
+    printf("%-5s  %02ld  ", stype, (long int) number);
+    if (f != 1) printf("  ");
+    else      printf("%s%s",(commonprops[number].is_long)?"L":" ",
+                            (commonprops[number].is_additive)?"A":" ");
+    printf("  %s\n", name);
 }
 
 extern void make_attribute(void)
@@ -134,6 +144,7 @@ more than",
 
     get_next_token();
     i = token_value; name = token_text;
+    /* We hold onto token_text through the end of this Property directive, which should be okay. */
     if (token_type != SYMBOL_TT)
     {   discard_token_location(beginning_debug_location);
         ebf_error("new attribute name", token_text);
@@ -141,9 +152,9 @@ more than",
         put_token_back();
         return;
     }
-    if (!(sflags[i] & UNKNOWN_SFLAG))
+    if (!(symbols[i].flags & UNKNOWN_SFLAG))
     {   discard_token_location(beginning_debug_location);
-        ebf_symbol_error("new attribute name", token_text, typename(stypes[i]), slines[i]);
+        ebf_symbol_error("new attribute name", token_text, typename(symbols[i].type), symbols[i].line);
         panic_mode_error_recovery(); 
         put_token_back();
         return;
@@ -156,7 +167,7 @@ more than",
     if ((token_type == DIR_KEYWORD_TT) && (token_value == ALIAS_DK))
     {   get_next_token();
         if (!((token_type == SYMBOL_TT)
-              && (stypes[token_value] == ATTRIBUTE_T)))
+              && (symbols[token_value].type == ATTRIBUTE_T)))
         {   discard_token_location(beginning_debug_location);
             ebf_error("an existing attribute name after 'alias'",
                 token_text);
@@ -164,9 +175,9 @@ more than",
             put_token_back();
             return;
         }
-        assign_symbol(i, svals[token_value], ATTRIBUTE_T);
-        sflags[token_value] |= ALIASED_SFLAG;
-        sflags[i] |= ALIASED_SFLAG;
+        assign_symbol(i, symbols[token_value].value, ATTRIBUTE_T);
+        symbols[token_value].flags |= ALIASED_SFLAG;
+        symbols[i].flags |= ALIASED_SFLAG;
     }
     else
     {   assign_symbol(i, no_attributes++, ATTRIBUTE_T);
@@ -176,62 +187,82 @@ more than",
     if (debugfile_switch)
     {   debug_file_printf("<attribute>");
         debug_file_printf("<identifier>%s</identifier>", name);
-        debug_file_printf("<value>%d</value>", svals[i]);
+        debug_file_printf("<value>%d</value>", symbols[i].value);
         write_debug_locations(get_token_location_end(beginning_debug_location));
         debug_file_printf("</attribute>");
     }
 
-    trace_s(name, svals[i], 0);
+    trace_s(name, symbols[i].value, 0);
     return;
 }
 
+/* Format:
+   Property [long] [additive] name
+   Property [long] [additive] name alias oldname
+   Property [long] [additive] name defaultvalue
+   Property [long] individual name
+ */
 extern void make_property(void)
 {   int32 default_value, i;
-    int additive_flag=FALSE; char *name;
-    assembly_operand AO;
+    int keywords, prevkeywords;
+    char *name;
+    int namelen;
+    int additive_flag, indiv_flag;
     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;
-        }
-    }
-
+    /* The next bit is tricky. We want to accept any number of the keywords
+       "long", "additive", "individual" before the property name. But we
+       also want to accept "Property long" -- that's a legitimate
+       property name.
+       The solution is to keep track of which keywords we've seen in
+       a bitmask, and another for one token previous. That way we
+       can back up one token if there's no name visible. */
+    keywords = prevkeywords = 0;
     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;
+        if ((token_type == DIR_KEYWORD_TT) && (token_value == LONG_DK)) {
+            prevkeywords = keywords;
+            keywords |= 1;
+        }
+        else if ((token_type == DIR_KEYWORD_TT) && (token_value == ADDITIVE_DK)) {
+            prevkeywords = keywords;
+            keywords |= 2;
+        }
+        else if ((token_type == DIR_KEYWORD_TT) && (token_value == INDIVIDUAL_DK)) {
+            prevkeywords = keywords;
+            keywords |= 4;
+        }
+        else {
+            break;
+        }
     } while (TRUE);
-
+    
+    /* Re-parse the name with keywords turned off. (This allows us to
+       accept a property name like "table".) */
     put_token_back();
     directive_keywords.enabled = FALSE;
     get_next_token();
 
+    if (token_type != SYMBOL_TT && keywords) {
+        /* This can't be a name. Try putting back the last keyword. */
+        keywords = prevkeywords;
+        put_token_back();
+        put_token_back();
+        get_next_token();
+    }
+
+    additive_flag = indiv_flag = FALSE;
+    if (keywords & 1)
+        obsolete_warning("all properties are now automatically 'long'");
+    if (keywords & 2)
+        additive_flag = TRUE;
+    if (keywords & 4)
+        indiv_flag = TRUE;
+    
     i = token_value; name = token_text;
+    /* We hold onto token_text through the end of this Property directive, which should be okay. */
     if (token_type != SYMBOL_TT)
     {   discard_token_location(beginning_debug_location);
         ebf_error("new property name", token_text);
@@ -239,19 +270,50 @@ Advanced game to get an extra 62)");
         put_token_back();
         return;
     }
-    if (!(sflags[i] & UNKNOWN_SFLAG))
+    if (!(symbols[i].flags & UNKNOWN_SFLAG))
     {   discard_token_location(beginning_debug_location);
-        ebf_symbol_error("new property name", token_text, typename(stypes[i]), slines[i]);
+        ebf_symbol_error("new property name", token_text, typename(symbols[i].type), symbols[i].line);
         panic_mode_error_recovery();
         put_token_back();
         return;
     }
 
+    if (indiv_flag) {
+        int this_identifier_number;
+        
+        if (additive_flag)
+        {   error("'individual' incompatible with 'additive'");
+            panic_mode_error_recovery();
+            put_token_back();
+            return;
+        }
+
+        this_identifier_number = no_individual_properties++;
+        assign_symbol(i, this_identifier_number, INDIVIDUAL_PROPERTY_T);
+        if (debugfile_switch) {
+            debug_file_printf("<property>");
+            debug_file_printf
+                ("<identifier>%s</identifier>", name);
+            debug_file_printf
+                ("<value>%d</value>", this_identifier_number);
+            debug_file_printf("</property>");
+        }
+        trace_s(name, symbols[i].value, 2);
+        return;        
+    }
+
     directive_keywords.enabled = TRUE;
     get_next_token();
     directive_keywords.enabled = FALSE;
 
-    if (strcmp(name+strlen(name)-3, "_to") == 0) sflags[i] |= STAR_SFLAG;
+    namelen = strlen(name);
+    if (namelen > 3 && strcmp(name+namelen-3, "_to") == 0) {
+        /* Direction common properties "n_to", etc are compared in some
+           libraries. They have STAR_SFLAG to tell us to skip a warning. */
+        symbols[i].flags |= STAR_SFLAG;
+    }
+
+    /* Now we might have "alias" or a default value (but not both). */
 
     if ((token_type == DIR_KEYWORD_TT) && (token_value == ALIAS_DK))
     {   discard_token_location(beginning_debug_location);
@@ -263,7 +325,7 @@ Advanced game to get an extra 62)");
         }
         get_next_token();
         if (!((token_type == SYMBOL_TT)
-            && (stypes[token_value] == PROPERTY_T)))
+            && (symbols[token_value].type == PROPERTY_T)))
         {   ebf_error("an existing property name after 'alias'",
                 token_text);
             panic_mode_error_recovery();
@@ -271,59 +333,88 @@ Advanced game to get an extra 62)");
             return;
         }
 
-        assign_symbol(i, svals[token_value], PROPERTY_T);
-        trace_s(name, svals[i], 1);
-        sflags[token_value] |= ALIASED_SFLAG;
-        sflags[i] |= ALIASED_SFLAG;
+        assign_symbol(i, symbols[token_value].value, PROPERTY_T);
+        trace_s(name, symbols[i].value, 1);
+        symbols[token_value].flags |= ALIASED_SFLAG;
+        symbols[i].flags |= ALIASED_SFLAG;
         return;
     }
 
+    /* We now know we're allocating a new common property. Make sure 
+       there's room. */
+    if (!glulx_mode) {
+        if (no_properties==((version_number==3)?32:64))
+        {   discard_token_location(beginning_debug_location);
+            /* The maximum listed here includes "name" but not the 
+               unused zero value or the two hidden properties (class
+               inheritance and indiv table). */
+            if (version_number==3)
+                error("All 29 properties already declared (compile as \
+Advanced game to get 32 more)");
+            else
+                error("All 61 properties already declared");
+            panic_mode_error_recovery();
+            put_token_back();
+            return;
+        }
+    }
+    else {
+        if (no_properties==INDIV_PROP_START) {
+            char error_b[128];
+            discard_token_location(beginning_debug_location);
+            sprintf(error_b,
+                "All %d properties already declared (increase INDIV_PROP_START to get more)",
+                INDIV_PROP_START-3);
+            error(error_b);
+            panic_mode_error_recovery(); 
+            put_token_back();
+            return;
+        }
+    }
+
     default_value = 0;
     put_token_back();
 
     if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
-    {   AO = parse_expression(CONSTANT_CONTEXT);
+    {
+        assembly_operand 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;
+    commonprops[no_properties].default_value = default_value;
+    commonprops[no_properties].is_long = TRUE;
+    commonprops[no_properties].is_additive = 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]);
+        debug_file_printf("<value>%d</value>", symbols[i].value);
         write_debug_locations
             (get_token_location_end(beginning_debug_location));
         debug_file_printf("</property>");
     }
 
-    trace_s(name, svals[i], 1);
+    trace_s(name, symbols[i].value, 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
+commonpropinfo *commonprops;            /* Info about common properties
+                                           (fixed allocation of 
+                                           INDIV_PROP_START entries) */
+
+uchar *properties_table;               /* Holds the table of property values
                                           (holding one block for each object
                                           and coming immediately after the
                                           object tree in Z-memory)           */
+memory_list properties_table_memlist;
 int properties_table_size;             /* Number of bytes in this table      */
 
 /* ------------------------------------------------------------------------- */
@@ -363,6 +454,7 @@ 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           */
+       memory_list individuals_table_memlist;
        int i_m;                        /* Write mark position in the above   */
        int individuals_length;         /* Extent of individuals_table        */
 
@@ -370,13 +462,16 @@ static int individual_prop_table_size; /* Size of the table of individual
 /*   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;
-
+objecttz     *objectsz;              /* Allocated to no_objects; Z-code only */
+memory_list objectsz_memlist;
+objecttg     *objectsg;              /* Allocated to no_objects; Glulx only  */
+static memory_list objectsg_memlist;
+uchar        *objectatts;            /* Allocated to no_objects; Glulx only  */
+static memory_list objectatts_memlist;
+static int   *classes_to_inherit_from; /* Allocated to no_classes_to_inherit_from */
+static memory_list classes_to_inherit_from_memlist;
+classinfo    *class_info;            /* Allocated up to no_classes           */
+memory_list   class_info_memlist;
 
 /* ------------------------------------------------------------------------- */
 /*   Tracing for compiler maintenance                                        */
@@ -384,10 +479,24 @@ int32        *class_begins_at;
 
 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);
+    printf("Object tree:\n");
+    printf("obj name                             par nxt chl:\n");
+    for (i=0; i<no_objects; i++) {
+        if (!glulx_mode) {
+            int sym = objectsz[i].symbol;
+            char *symname = ((sym > 0) ? symbols[sym].name : "...");
+            printf("%3d %-32s %3d %3d %3d\n",
+                i+1, symname,
+                objectsz[i].parent, objectsz[i].next, objectsz[i].child);
+        }
+        else {
+            int sym = objectsg[i].symbol;
+            char *symname = ((sym > 0) ? symbols[sym].name : "...");
+            printf("%3d %-32s %3d %3d %3d\n",
+                i+1, symname,
+                objectsg[i].parent, objectsg[i].next, objectsg[i].child);
+        }
+    }
 }
 
 /* ------------------------------------------------------------------------- */
@@ -437,8 +546,8 @@ static void property_inheritance_z(void)
     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);
+        mark = class_info[classes_to_inherit_from[class] - 1].begins_at;
+        class_prop_block = (properties_table + mark);
 
         while (class_prop_block[j]!=0)
         {   if (version_number == 3)
@@ -463,6 +572,8 @@ static void property_inheritance_z(void)
             prop_in_current_defn = FALSE;
 
             kmax = full_object.l;
+            if (kmax > 64)
+                fatalerror("More than 64 property entries in an object");
 
             for (k=0; k<kmax; k++)
                 if (full_object.pp[k].num == prop_number)
@@ -470,7 +581,7 @@ static void property_inheritance_z(void)
 
                     /*  (Note that the built-in "name" property is additive) */
 
-                    if ((prop_number==1) || (prop_is_additive[prop_number]))
+                    if ((prop_number==1) || (commonprops[prop_number].is_additive))
                     {
                         /*  The additive case: we accumulate the class
                             property values onto the end of the full_object
@@ -483,10 +594,9 @@ static void property_inheritance_z(void)
 so many values that the list has overflowed the maximum 32 entries");
                                 break;
                             }
-                            full_object.pp[k].ao[i].value = mark + j;
+                            INITAOTV(&full_object.pp[k].ao[i], LONG_CONSTANT_OT, 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;
                     }
@@ -499,7 +609,6 @@ so many values that the list has overflowed the maximum 32 entries");
 
                     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
@@ -510,35 +619,31 @@ so many values that the list has overflowed the maximum 32 entries");
                         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))
+                        while ((individuals_table[z]!=0)||(individuals_table[z+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])
+                                if (individuals_table[l] == individuals_table[z]
+                                    && individuals_table[l + 1] == individuals_table[z+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++)
+                                ensure_memory_list_available(&individuals_table_memlist, i_m+3+individuals_table[z+2]);
+                                individuals_table[i_m++] = individuals_table[z];
+                                individuals_table[i_m++] = individuals_table[z+1];
+                                individuals_table[i_m++] = individuals_table[z+2];
+                                for (y=0;y < individuals_table[z+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;
+                            z += individuals_table[z+2] + 3;
                         }
                         individuals_length = i_m;
                     }
@@ -556,18 +661,19 @@ so many values that the list has overflowed the maximum 32 entries");
                     a new property added to full_object                      */
 
                 k=full_object.l++;
+                if (k >= 64)
+                    fatalerror("More than 64 property entries in an object");
                 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;
+                {
+                    INITAOTV(&full_object.pp[k].ao[i], LONG_CONSTANT_OT, 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
@@ -577,34 +683,28 @@ so many values that the list has overflowed the maximum 32 entries");
                     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;
+                        INITAOTV(&full_object.pp[k].ao[0], LONG_CONSTANT_OT, 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))
+                    while ((individuals_table[z]!=0)||(individuals_table[z+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++)
+                        ensure_memory_list_available(&individuals_table_memlist, i_m+3+individuals_table[z+2]);
+                        individuals_table[i_m++] = individuals_table[z];
+                        individuals_table[i_m++] = individuals_table[z+1];
+                        individuals_table[i_m++] = individuals_table[z+2];
+                        for (y=0;y < individuals_table[z+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;
+                        z += individuals_table[z+2] + 3;
                     }
                     individuals_length = i_m;
                 }
@@ -614,9 +714,7 @@ so many values that the list has overflowed the maximum 32 entries");
 
     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);
+        ensure_memory_list_available(&individuals_table_memlist, i_m+2);
 
         individuals_table[i_m++] = 0;
         individuals_table[i_m++] = 0;
@@ -641,8 +739,8 @@ static void property_inheritance_g(void)
   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);
+    mark = class_info[classes_to_inherit_from[class] - 1].begins_at;
+    cpb = (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);
@@ -675,7 +773,7 @@ static void property_inheritance_g(void)
       if (prop_in_current_defn) {
         if ((prop_number==1)
           || (prop_number < INDIV_PROP_START 
-            && prop_is_additive[prop_number])) {
+            && commonprops[prop_number].is_additive)) {
           /*  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
@@ -694,21 +792,18 @@ static void property_inheritance_g(void)
             }
           }
           k = full_object_g.numprops++;
+          ensure_memory_list_available(&full_object_g.props_memlist, k+1);
           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);
-          }
-
+          
+          ensure_memory_list_available(&full_object_g.propdata_memlist, full_object_g.propdatasize + prop_length);
           for (i=0; i<prop_length; i++) {
             int ppos = full_object_g.propdatasize++;
-            full_object_g.propdata[ppos].value = prop_addr + 4*i;
+            INITAOTV(&full_object_g.propdata[ppos], CONSTANT_OT, prop_addr + 4*i);
             full_object_g.propdata[ppos].marker = INHERIT_MV;
-            full_object_g.propdata[ppos].type = CONSTANT_OT;
           }
         }
         else {
@@ -722,27 +817,21 @@ static void property_inheritance_g(void)
                 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++;
+            ensure_memory_list_available(&full_object_g.props_memlist, k+1);
             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);
-            }
 
+            ensure_memory_list_available(&full_object_g.propdata_memlist, full_object_g.propdatasize + prop_length);
             for (i=0; i<prop_length; i++) {
               int ppos = full_object_g.propdatasize++;
-              full_object_g.propdata[ppos].value = prop_addr + 4*i;
+              INITAOTV(&full_object_g.propdata[ppos], CONSTANT_OT, 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);
-          }
     }
   }
   
@@ -752,27 +841,27 @@ static void property_inheritance_g(void)
 /*   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. */
+static int write_properties_between(int mark, int from, int to)
+{   int j, k, prop_number;
+
     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);
+            {
+                int prop_length = 2*full_object.pp[j].l;
+                ensure_memory_list_available(&properties_table_memlist, mark+2+prop_length);
                 if (version_number == 3)
-                    p[mark++] = prop_number + (prop_length - 1)*32;
+                    properties_table[mark++] = prop_number + (prop_length - 1)*32;
                 else
                 {   switch(prop_length)
                     {   case 1:
-                          p[mark++] = prop_number; break;
+                          properties_table[mark++] = prop_number; break;
                         case 2:
-                          p[mark++] = prop_number + 0x40; break;
+                          properties_table[mark++] = prop_number + 0x40; break;
                         default:
-                          p[mark++] = prop_number + 0x80;
-                          p[mark++] = prop_length + 0x80; break;
+                          properties_table[mark++] = prop_number + 0x80;
+                          properties_table[mark++] = prop_length + 0x80; break;
                     }
                 }
 
@@ -780,14 +869,15 @@ static int write_properties_between(uchar *p, int mark, int from, int to)
                 {   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;
+                    properties_table[mark++] = full_object.pp[j].ao[k].value/256;
+                    properties_table[mark++] = full_object.pp[j].ao[k].value%256;
                 }
             }
         }
     }
 
-    p[mark++]=0;
+    ensure_memory_list_available(&properties_table_memlist, mark+1);
+    properties_table[mark++]=0;
     return(mark);
 }
 
@@ -801,28 +891,31 @@ static int write_property_block_z(char *shortname)
         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,STRCTX_OBJNAME);
-        if (!tmp) error ("Short name of object exceeded 765 Z-characters");
-        i = subtract_pointers(tmp,(p+mark+1));
-        p[mark] = i/2;
+    {
+        i = translate_text(510,shortname,STRCTX_OBJNAME);
+        if (i < 0) {
+            error ("Short name of object exceeded 765 Z-characters");
+            i = 0;
+        }
+        ensure_memory_list_available(&properties_table_memlist, mark+1+i);
+        memcpy(properties_table + mark+1, translated_text, i);
+        properties_table[mark] = i/2;
         mark += i+1;
     }
     if (current_defn_is_class)
-    {   mark = write_properties_between(p,mark,3,3);
+    {   mark = write_properties_between(mark,3,3);
+        ensure_memory_list_available(&properties_table_memlist, mark+6);
         for (i=0;i<6;i++)
-            p[mark++] = full_object.atts[i];
-        class_begins_at[no_classes++] = mark;
+            properties_table[mark++] = full_object.atts[i];
+        ensure_memory_list_available(&class_info_memlist, no_classes+1);
+        class_info[no_classes++].begins_at = mark;
     }
 
-    mark = write_properties_between(p, mark, 1, (version_number==3)?31:63);
+    mark = write_properties_between(mark, 1, (version_number==3)?31:63);
 
     i = mark - properties_table_size;
     properties_table_size = mark;
@@ -859,12 +952,13 @@ static int32 write_property_block_g(void)
   int ix, jx, kx, totalprops;
   int32 mark = properties_table_size;
   int32 datamark;
-  uchar *p = (uchar *) properties_table;
 
   if (current_defn_is_class) {
+    ensure_memory_list_available(&properties_table_memlist, mark+NUM_ATTR_BYTES);
     for (i=0;i<NUM_ATTR_BYTES;i++)
-      p[mark++] = full_object_g.atts[i];
-    class_begins_at[no_classes++] = mark;
+      properties_table[mark++] = full_object_g.atts[i];
+    ensure_memory_list_available(&class_info_memlist, no_classes+1);
+    class_info[no_classes++].begins_at = mark;
   }
 
   qsort(full_object_g.props, full_object_g.numprops, sizeof(propg), 
@@ -885,9 +979,8 @@ static int32 write_property_block_g(void)
   }
 
   /* 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);
+  ensure_memory_list_available(&properties_table_memlist, mark+4);
+  WriteInt32(properties_table+mark, totalprops);
   mark += 4;
 
   datamark = mark + 10*totalprops;
@@ -903,11 +996,10 @@ static int32 write_property_block_g(void)
         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);
+      ensure_memory_list_available(&properties_table_memlist, datamark+4*full_object_g.props[jx].datalen);
       for (kx=0; kx<full_object_g.props[jx].datalen; kx++) {
         int32 val = full_object_g.propdata[datastart+kx].value;
-        WriteInt32(p+datamark, val);
+        WriteInt32(properties_table+datamark, val);
         if (full_object_g.propdata[datastart+kx].marker != 0)
           backpatch_zmachine(full_object_g.propdata[datastart+kx].marker,
             PROP_ZA, datamark);
@@ -915,15 +1007,14 @@ static int32 write_property_block_g(void)
         datamark += 4;
       }
     }
-    if (mark+10 >= MAX_PROP_TABLE_SIZE)
-        memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE);
-    WriteInt16(p+mark, propnum);
+    ensure_memory_list_available(&properties_table_memlist, mark+10);
+    WriteInt16(properties_table+mark, propnum);
     mark += 2;
-    WriteInt16(p+mark, totallen);
+    WriteInt16(properties_table+mark, totallen);
     mark += 2;
-    WriteInt32(p+mark, datamarkstart); 
+    WriteInt32(properties_table+mark, datamarkstart); 
     mark += 4;
-    WriteInt16(p+mark, flags);
+    WriteInt16(properties_table+mark, flags);
     mark += 2;
   }
 
@@ -944,6 +1035,10 @@ static void manufacture_object_z(void)
     segment_markers.enabled = FALSE;
     directives.enabled = TRUE;
 
+    ensure_memory_list_available(&objectsz_memlist, no_objects+1);
+
+    objectsz[no_objects].symbol = full_object.symbol;
+    
     property_inheritance_z();
 
     objectsz[no_objects].parent = parent_of_this_obj;
@@ -967,8 +1062,6 @@ static void manufacture_object_z(void)
     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;
@@ -985,6 +1078,11 @@ static void manufacture_object_g(void)
     segment_markers.enabled = FALSE;
     directives.enabled = TRUE;
 
+    ensure_memory_list_available(&objectsg_memlist, no_objects+1);
+    ensure_memory_list_available(&objectatts_memlist, no_objects+1);
+    
+    objectsg[no_objects].symbol = full_object_g.symbol;
+    
     property_inheritance_g();
 
     objectsg[no_objects].parent = parent_of_this_obj;
@@ -1013,8 +1111,6 @@ static void manufacture_object_g(void)
     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++) 
@@ -1072,10 +1168,10 @@ static void properties_segment_z(int this_segment)
             return;
         }
 
-        individual_property = (stypes[token_value] != PROPERTY_T);
+        individual_property = (symbols[token_value].type != PROPERTY_T);
 
         if (individual_property)
-        {   if (sflags[token_value] & UNKNOWN_SFLAG)
+        {   if (symbols[token_value].flags & UNKNOWN_SFLAG)
             {   this_identifier_number = no_individual_properties++;
                 assign_symbol(token_value, this_identifier_number,
                     INDIVIDUAL_PROPERTY_T);
@@ -1089,12 +1185,13 @@ static void properties_segment_z(int this_segment)
                     debug_file_printf("</property>");
                 }
 
+                trace_s(token_text, symbols[token_value].value, 2);
             }
             else
-            {   if (stypes[token_value]==INDIVIDUAL_PROPERTY_T)
-                    this_identifier_number = svals[token_value];
+            {   if (symbols[token_value].type==INDIVIDUAL_PROPERTY_T)
+                    this_identifier_number = symbols[token_value].value;
                 else
-                {   ebf_symbol_error("property name", token_text, typename(stypes[token_value]), slines[token_value]);
+                {   ebf_symbol_error("property name", token_text, typename(symbols[token_value].type), symbols[token_value].line);
                     return;
                 }
             }
@@ -1104,16 +1201,18 @@ static void properties_segment_z(int this_segment)
             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;
+            {
+                int k=full_object.l++;
+                if (k >= 64)
+                    fatalerror("More than 64 property entries in an object");
+                full_object.pp[k].num = 3;
+                full_object.pp[k].l = 1;
+                INITAOTV(&full_object.pp[k].ao[0], LONG_CONSTANT_OT, individuals_length);
+                full_object.pp[k].ao[0].marker = INDIVPT_MV;
 
                 i_m = individuals_length;
-                full_object.l++;
             }
+            ensure_memory_list_available(&individuals_table_memlist, i_m+3);
             individuals_table[i_m] = this_identifier_number/256;
             if (this_segment == PRIVATE_SEGMENT)
                 individuals_table[i_m] |= 0x80;
@@ -1123,7 +1222,7 @@ static void properties_segment_z(int this_segment)
             individuals_table[i_m+2] = 0;
         }
         else
-        {   if (sflags[token_value] & UNKNOWN_SFLAG)
+        {   if (symbols[token_value].flags & UNKNOWN_SFLAG)
             {   error_named("No such property name as", token_text);
                 return;
             }
@@ -1133,30 +1232,32 @@ 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];
+            property_number = symbols[token_value].value;
 
             next_prop=full_object.l++;
+            if (next_prop >= 64)
+                fatalerror("More than 64 property entries in an object");
             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]);
+                    symbols[token_value].name);
             }
             else
-            if (svals[defined_this_segment[i]] == svals[token_value])
-            {   char error_b[128];
+            if (symbols[defined_this_segment[i]].value == symbols[token_value].value)
+            {   char error_b[128+2*MAX_IDENTIFIER_LENGTH];
                 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]);
+                    symbols[defined_this_segment[i]].name,
+                    symbols[token_value].name);
                 error(error_b);
             }
 
         property_name_symbol = token_value;
-        sflags[token_value] |= USED_SFLAG;
+        symbols[token_value].flags |= USED_SFLAG;
 
         length=0;
         do
@@ -1176,18 +1277,24 @@ the names '%s' and '%s' actually refer to the same property",
                 warning ("'name' property should only contain dictionary words");
 
             if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
-            {   char embedded_name[80];
+            {
+                char *prefix, *sep, *sym;
+                sym = symbols[property_name_symbol].name;
                 if (current_defn_is_class)
-                {   sprintf(embedded_name,
-                        "%s::%s", classname_text,
-                        (char *) symbs[property_name_symbol]);
+                {
+                    prefix = symbols[current_classname_symbol].name;
+                    sep = "::";
                 }
                 else
-                {   sprintf(embedded_name,
-                        "%s.%s", objectname_text,
-                        (char *) symbs[property_name_symbol]);
+                {
+                    prefix = current_object_name.data;
+                    sep = ".";
                 }
-                AO.value = parse_routine(NULL, TRUE, embedded_name, FALSE, -1);
+                ensure_memory_list_available(&embedded_function_name, strlen(prefix)+strlen(sep)+strlen(sym)+1);
+                sprintf(embedded_function_name.data, "%s%s%s", prefix, sep, sym);
+
+                /* parse_routine() releases lexer text! */
+                AO.value = parse_routine(NULL, TRUE, embedded_function_name.data, FALSE, -1);
                 AO.type = LONG_CONSTANT_OT;
                 AO.marker = IROUTINE_MV;
 
@@ -1217,7 +1324,7 @@ the names '%s' and '%s' actually refer to the same property",
             {   if (length!=0)
                 {
                     if ((token_type == SYMBOL_TT)
-                        && (stypes[token_value]==PROPERTY_T))
+                        && (symbols[token_value].type==PROPERTY_T))
                     {
                         /*  This is not necessarily an error: it's possible
                             to imagine a property whose value is a list
@@ -1239,7 +1346,7 @@ the names '%s' and '%s' actually refer to the same property",
 
             if (length == 64)
             {   error_named("Limit (of 32 values) exceeded for property",
-                    (char *) symbs[property_name_symbol]);
+                    symbols[property_name_symbol].name);
                 break;
             }
 
@@ -1247,6 +1354,7 @@ the names '%s' and '%s' actually refer to the same property",
             {   if (AO.marker != 0)
                     backpatch_zmachine(AO.marker, INDIVIDUAL_PROP_ZA,
                         i_m+3+length);
+                ensure_memory_list_available(&individuals_table_memlist, i_m+3+length+2);
                 individuals_table[i_m+3+length++] = AO.value/256;
                 individuals_table[i_m+3+length++] = AO.value%256;
             }
@@ -1267,13 +1375,14 @@ the names '%s' and '%s' actually refer to the same property",
 
         if (length == 0)
         {   if (individual_property)
-            {   individuals_table[i_m+3+length++] = 0;
+            {
+                ensure_memory_list_available(&individuals_table_memlist, i_m+3+length+2);
+                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;
+            {
+                INITAOTV(&full_object.pp[next_prop].ao[0], LONG_CONSTANT_OT, 0);
                 length = 2;
             }
         }
@@ -1283,16 +1392,14 @@ the names '%s' and '%s' actually refer to the same property",
             {
        warning_named("Version 3 limit of 4 values per property exceeded \
 (use -v5 to get 32), so truncating property",
-                    (char *) symbs[property_name_symbol]);
+                    symbols[property_name_symbol].name);
                 length = 8;
             }
         }
 
         if (individual_property)
         {
-            if (individuals_length+length+3 > MAX_INDIV_PROP_TABLE_SIZE)
-                memoryerror("MAX_INDIV_PROP_TABLE_SIZE",
-                    MAX_INDIV_PROP_TABLE_SIZE);
+            ensure_memory_list_available(&individuals_table_memlist, individuals_length+length+3);
             individuals_table[i_m + 2] = length;
             individuals_length += length+3;
             i_m = individuals_length;
@@ -1336,10 +1443,10 @@ static void properties_segment_g(int this_segment)
             return;
         }
 
-        individual_property = (stypes[token_value] != PROPERTY_T);
+        individual_property = (symbols[token_value].type != PROPERTY_T);
 
         if (individual_property)
-        {   if (sflags[token_value] & UNKNOWN_SFLAG)
+        {   if (symbols[token_value].flags & UNKNOWN_SFLAG)
             {   this_identifier_number = no_individual_properties++;
                 assign_symbol(token_value, this_identifier_number,
                     INDIVIDUAL_PROPERTY_T);
@@ -1353,12 +1460,13 @@ static void properties_segment_g(int this_segment)
                     debug_file_printf("</property>");
                 }
 
+                trace_s(token_text, symbols[token_value].value, 2);
             }
             else
-            {   if (stypes[token_value]==INDIVIDUAL_PROPERTY_T)
-                    this_identifier_number = svals[token_value];
+            {   if (symbols[token_value].type==INDIVIDUAL_PROPERTY_T)
+                    this_identifier_number = symbols[token_value].value;
                 else
-                {   ebf_symbol_error("property name", token_text, typename(stypes[token_value]), slines[token_value]);
+                {   ebf_symbol_error("property name", token_text, typename(symbols[token_value].type), symbols[token_value].line);
                     return;
                 }
             }
@@ -1366,9 +1474,10 @@ static void properties_segment_g(int this_segment)
             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];
+            property_number = symbols[token_value].value;
 
             next_prop=full_object_g.numprops++;
+            ensure_memory_list_available(&full_object_g.props_memlist, next_prop+1);
             full_object_g.props[next_prop].num = property_number;
             full_object_g.props[next_prop].flags = 
               ((this_segment == PRIVATE_SEGMENT) ? 1 : 0);
@@ -1377,7 +1486,7 @@ static void properties_segment_g(int this_segment)
             full_object_g.props[next_prop].datalen = 0;
         }
         else
-        {   if (sflags[token_value] & UNKNOWN_SFLAG)
+        {   if (symbols[token_value].flags & UNKNOWN_SFLAG)
             {   error_named("No such property name as", token_text);
                 return;
             }
@@ -1388,9 +1497,10 @@ 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];
+            property_number = symbols[token_value].value;
 
             next_prop=full_object_g.numprops++;
+            ensure_memory_list_available(&full_object_g.props_memlist, next_prop+1);
             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;
@@ -1401,25 +1511,21 @@ not 'private':", token_text);
         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]);
+                    symbols[token_value].name);
             }
             else
-            if (svals[defined_this_segment[i]] == svals[token_value])
-            {   char error_b[128];
+            if (symbols[defined_this_segment[i]].value == symbols[token_value].value)
+            {   char error_b[128+2*MAX_IDENTIFIER_LENGTH];
                 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]);
+                    symbols[defined_this_segment[i]].name,
+                    symbols[token_value].name);
                 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;
+        symbols[token_value].flags |= USED_SFLAG;
 
         length=0;
         do
@@ -1439,19 +1545,25 @@ the names '%s' and '%s' actually refer to the same property",
                 warning ("'name' property should only contain dictionary words");
 
             if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
-            {   char embedded_name[80];
+            {
+                char *prefix, *sep, *sym;
+                sym = symbols[property_name_symbol].name;
                 if (current_defn_is_class)
-                {   sprintf(embedded_name,
-                        "%s::%s", classname_text,
-                        (char *) symbs[property_name_symbol]);
+                {
+                    prefix = symbols[current_classname_symbol].name;
+                    sep = "::";
                 }
                 else
-                {   sprintf(embedded_name,
-                        "%s.%s", objectname_text,
-                        (char *) symbs[property_name_symbol]);
+                {
+                    prefix = current_object_name.data;
+                    sep = ".";
                 }
-                AO.value = parse_routine(NULL, TRUE, embedded_name, FALSE, -1);
-                AO.type = CONSTANT_OT; 
+                ensure_memory_list_available(&embedded_function_name, strlen(prefix)+strlen(sep)+strlen(sym)+1);
+                sprintf(embedded_function_name.data, "%s%s%s", prefix, sep, sym);
+
+                INITAOT(&AO, CONSTANT_OT);
+                /* parse_routine() releases lexer text! */
+                AO.value = parse_routine(NULL, TRUE, embedded_function_name.data, FALSE, -1);
                 AO.marker = IROUTINE_MV;
 
                 directives.enabled = FALSE;
@@ -1480,7 +1592,7 @@ the names '%s' and '%s' actually refer to the same property",
             {   if (length!=0)
                 {
                     if ((token_type == SYMBOL_TT)
-                        && (stypes[token_value]==PROPERTY_T))
+                        && (symbols[token_value].type==PROPERTY_T))
                     {
                         /*  This is not necessarily an error: it's possible
                             to imagine a property whose value is a list
@@ -1502,13 +1614,11 @@ the names '%s' and '%s' actually refer to the same property",
 
             if (length == 32768) /* VENEER_CONSTRAINT_ON_PROP_TABLE_SIZE? */
             {   error_named("Limit (of 32768 values) exceeded for property",
-                    (char *) symbs[property_name_symbol]);
+                    symbols[property_name_symbol].name);
                 break;
             }
 
-            if (full_object_g.propdatasize >= MAX_OBJ_PROP_TABLE_SIZE) {
-              memoryerror("MAX_OBJ_PROP_TABLE_SIZE",MAX_OBJ_PROP_TABLE_SIZE);
-            }
+            ensure_memory_list_available(&full_object_g.propdata_memlist, full_object_g.propdatasize+1);
 
             full_object_g.propdata[full_object_g.propdatasize++] = AO;
             length += 1;
@@ -1526,9 +1636,8 @@ the names '%s' and '%s' actually refer to the same property",
         if (length == 0)
         {
             assembly_operand AO;
-            AO.value = 0;
-            AO.type = CONSTANT_OT;
-            AO.marker = 0;
+            INITAOTV(&AO, CONSTANT_OT, 0);
+            ensure_memory_list_available(&full_object_g.propdata_memlist, full_object_g.propdatasize+1);
             full_object_g.propdata[full_object_g.propdatasize++] = AO;
             length += 1;
         }
@@ -1583,13 +1692,13 @@ static void attributes_segment(void)
         }
 
         if ((token_type != SYMBOL_TT)
-            || (stypes[token_value] != ATTRIBUTE_T))
+            || (symbols[token_value].type != ATTRIBUTE_T))
         {   ebf_error("name of an already-declared attribute", token_text);
             return;
         }
 
-        attribute_number = svals[token_value];
-        sflags[token_value] |= USED_SFLAG;
+        attribute_number = symbols[token_value].value;
+        symbols[token_value].flags |= USED_SFLAG;
 
         if (!glulx_mode) {
             bitmask = (1 << (7-attribute_number%8));
@@ -1623,7 +1732,7 @@ static void add_class_to_inheritance_list(int class_number)
         to be translated into its actual class number:                       */
 
     for (i=0;i<no_classes;i++)
-        if (class_number == class_object_numbers[i])
+        if (class_number == class_info[i].object_number)
         {   class_number = i+1;
             break;
         }
@@ -1631,6 +1740,8 @@ static void add_class_to_inheritance_list(int class_number)
     /*  Remember the inheritance list so that property inheritance can
         be sorted out later on, when the definition has been finished:       */
 
+    ensure_memory_list_available(&classes_to_inherit_from_memlist, no_classes_to_inherit_from+1);
+
     classes_to_inherit_from[no_classes_to_inherit_from++] = class_number;
 
     /*  Inheriting attributes from the class at once:                        */
@@ -1638,12 +1749,12 @@ static void add_class_to_inheritance_list(int class_number)
     if (!glulx_mode) {
         for (i=0; i<6; i++)
             full_object.atts[i]
-                |= properties_table[class_begins_at[class_number-1] - 6 + i];
+                |= properties_table[class_info[class_number-1].begins_at - 6 + i];
     }
     else {
         for (i=0; i<NUM_ATTR_BYTES; i++)
             full_object_g.atts[i]
-                |= properties_table[class_begins_at[class_number-1] 
+                |= properties_table[class_info[class_number-1].begins_at 
                     - NUM_ATTR_BYTES + i];
     }
 }
@@ -1664,13 +1775,17 @@ static void classes_segment(void)
         if ((token_type == SEP_TT) && (token_value == COMMA_SEP)) return;
 
         if ((token_type != SYMBOL_TT)
-            || (stypes[token_value] != CLASS_T))
+            || (symbols[token_value].type != CLASS_T))
         {   ebf_error("name of an already-declared class", token_text);
             return;
         }
+        if (current_defn_is_class && token_value == current_classname_symbol)
+        {   error("A class cannot inherit from itself");
+            return;
+        }
 
-        sflags[token_value] |= USED_SFLAG;
-        add_class_to_inheritance_list(svals[token_value]);
+        symbols[token_value].flags |= USED_SFLAG;
+        add_class_to_inheritance_list(symbols[token_value].value);
     } while (TRUE);
 }
 
@@ -1736,6 +1851,7 @@ static void initialise_full_object(void)
 {
   int i;
   if (!glulx_mode) {
+    full_object.symbol = 0;
     full_object.l = 0;
     full_object.atts[0] = 0;
     full_object.atts[1] = 0;
@@ -1745,6 +1861,7 @@ static void initialise_full_object(void)
     full_object.atts[5] = 0;
   }
   else {
+    full_object_g.symbol = 0;
     full_object_g.numprops = 0;
     full_object_g.propdatasize = 0;
     for (i=0; i<NUM_ATTR_BYTES; i++)
@@ -1755,15 +1872,13 @@ static void initialise_full_object(void)
 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];
     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);
+    ensure_memory_list_available(&class_info_memlist, no_classes+1);
 
     if (no_classes==VENEER_CONSTRAINT_ON_CLASSES)
         fatalerror("Inform's maximum possible number of classes (whatever \
@@ -1785,9 +1900,9 @@ inconvenience, please contact the maintainers.");
             panic_mode_error_recovery();
             return;
         }
-        if (!(sflags[token_value] & UNKNOWN_SFLAG))
+        if (!(symbols[token_value].flags & UNKNOWN_SFLAG))
         {   discard_token_location(beginning_debug_location);
-            ebf_symbol_error("new class name", token_text, typename(stypes[token_value]), slines[token_value]);
+            ebf_symbol_error("new class name", token_text, typename(symbols[token_value].type), symbols[token_value].line);
             panic_mode_error_recovery();
             return;
         }
@@ -1798,15 +1913,15 @@ inconvenience, please contact the maintainers.");
     strcpy(shortname_buffer, token_text);
 
     assign_symbol(token_value, class_number, CLASS_T);
-    classname_text = (char *) symbs[token_value];
+    current_classname_symbol = token_value;
 
     if (!glulx_mode) {
-        if (metaclass_flag) sflags[token_value] |= SYSTEM_SFLAG;
+        if (metaclass_flag) symbols[token_value].flags |= 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;
+        if (metaclass_flag) symbols[token_value].flags |= USED_SFLAG;
     }
 
     /*  "Class" (object 1) has no parent, whereas all other classes are
@@ -1816,7 +1931,9 @@ inconvenience, please contact the maintainers.");
     if (metaclass_flag) parent_of_this_obj = 0;
     else parent_of_this_obj = (module_switch)?MAXINTWORD:1;
 
-    class_object_numbers[no_classes] = class_number;
+    class_info[no_classes].object_number = class_number;
+    class_info[no_classes].symbol = current_classname_symbol;
+    class_info[no_classes].begins_at = 0;
 
     initialise_full_object();
 
@@ -1826,23 +1943,25 @@ inconvenience, please contact the maintainers.");
         since property 2 is always set to "additive" -- see below)           */
 
     if (!glulx_mode) {
+      full_object.symbol = current_classname_symbol;
       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;
+      INITAOTV(&full_object.pp[0].ao[0], LONG_CONSTANT_OT, no_objects + 1);
       full_object.pp[0].ao[0].marker = OBJECT_MV;
     }
     else {
+      full_object_g.symbol = current_classname_symbol;
       full_object_g.numprops = 1;
+      ensure_memory_list_available(&full_object_g.props_memlist, 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;
+      ensure_memory_list_available(&full_object_g.propdata_memlist, 1);
+      INITAOTV(&full_object_g.propdata[0], CONSTANT_OT, no_objects + 1);
       full_object_g.propdata[0].marker = OBJECT_MV;
     }
 
@@ -1898,16 +2017,20 @@ 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);
+    {
+        int namelen = strlen(shortname_buffer);
+        char *duplicate_name = my_malloc(namelen+16, "temporary storage for object duplicate names");
+        strcpy(duplicate_name, 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);
-            }
+        {
+            sprintf(duplicate_name+namelen, "_%d", n);
             make_object(FALSE, duplicate_name, class_number, class_number, -1);
         }
+        my_free(&duplicate_name, "temporary storage for object duplicate names");
     }
+
+    /* Finished building the class. */
+    current_classname_symbol = 0;
 }
 
 /* ------------------------------------------------------------------------- */
@@ -1936,16 +2059,13 @@ extern void make_object(int nearby_flag,
         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;
+    ensure_memory_list_available(&current_object_name, 32);
+    sprintf(current_object_name.data, "nameless_obj__%d", no_objects+1);
 
     current_defn_is_class = FALSE;
 
@@ -1986,12 +2106,13 @@ extern void make_object(int nearby_flag,
             ebf_error("name for new object or its textual short name",
                 token_text);
         }
-        else if (!(sflags[token_value] & UNKNOWN_SFLAG)) {
-            ebf_symbol_error("new object", token_text, typename(stypes[token_value]), slines[token_value]);
+        else if (!(symbols[token_value].flags & UNKNOWN_SFLAG)) {
+            ebf_symbol_error("new object", token_text, typename(symbols[token_value].type), symbols[token_value].line);
         }
         else
         {   internal_name_symbol = token_value;
-            strcpy(internal_name, token_text);
+            ensure_memory_list_available(&current_object_name, strlen(token_text)+1);
+            strcpy(current_object_name.data, token_text);
         }
     }
 
@@ -2009,7 +2130,7 @@ extern void make_object(int nearby_flag,
     }
     else
     {   if ((token_type != SYMBOL_TT)
-            || (sflags[token_value] & UNKNOWN_SFLAG))
+            || (symbols[token_value].flags & UNKNOWN_SFLAG))
         {   if (textual_name == NULL)
                 ebf_error("parent object or the object's textual short name",
                     token_text);
@@ -2028,10 +2149,10 @@ extern void make_object(int nearby_flag,
         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;
+        if ((symbols[token_value].type == OBJECT_T)
+            || (symbols[token_value].type == CLASS_T))
+        {   specified_parent = symbols[token_value].value;
+            symbols[token_value].flags |= USED_SFLAG;
         }
         else ebf_error("name of (the parent) object", token_text);
     }
@@ -2049,13 +2170,10 @@ extern void make_object(int nearby_flag,
     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]);
+                symbols[internal_name_symbol].name);
         else
             sprintf(shortname_buffer, "(%d)", no_objects+1);
     }
@@ -2116,6 +2234,11 @@ extern void make_object(int nearby_flag,
     }
 
     initialise_full_object();
+    if (!glulx_mode)
+        full_object.symbol = internal_name_symbol;
+    else
+        full_object_g.symbol = internal_name_symbol;
+
     if (instance_of != -1) add_class_to_inheritance_list(instance_of);
 
     if (specified_class == -1) parse_body_of_definition();
@@ -2124,11 +2247,12 @@ extern void make_object(int nearby_flag,
     if (debugfile_switch)
     {   debug_file_printf("<object>");
         if (internal_name_symbol > 0)
-        {   debug_file_printf("<identifier>%s</identifier>", internal_name);
+        {   debug_file_printf("<identifier>%s</identifier>",
+                 current_object_name.data);
         } else
         {   debug_file_printf
                 ("<identifier artificial=\"true\">%s</identifier>",
-                 internal_name);
+                 current_object_name.data);
         }
         debug_file_printf("<value>");
         write_debug_object_backpatch(no_objects + 1);
@@ -2151,29 +2275,51 @@ extern void make_object(int nearby_flag,
 extern void init_objects_vars(void)
 {
     properties_table = NULL;
-    prop_is_long = NULL;
-    prop_is_additive = NULL;
-    prop_default_value = NULL;
+    individuals_table = NULL;
+    commonprops = NULL;
 
     objectsz = NULL;
     objectsg = NULL;
     objectatts = NULL;
     classes_to_inherit_from = NULL;
-    class_begins_at = NULL;
+    class_info = NULL;
+
+    full_object_g.props = NULL;    
+    full_object_g.propdata = 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 */
+
+    /* The three predefined common properties: */
+    /* (Entry 0 is not used.) */
+
+    /* "name" */
+    commonprops[1].default_value = 0;
+    commonprops[1].is_long = TRUE;
+    commonprops[1].is_additive = TRUE;
+
+    /* class inheritance property */
+    commonprops[2].default_value = 0;
+    commonprops[2].is_long = TRUE;
+    commonprops[2].is_additive = TRUE;
+
+    /* instance variables table address */
+    /* (This property is only meaningful in Z-code; in Glulx its entry is
+       reserved but never used.) */
+    commonprops[3].default_value = 0;
+    commonprops[3].is_long = TRUE;
+    commonprops[3].is_additive = FALSE;
+                                         
     no_properties = 4;
 
     if (debugfile_switch)
-    {   debug_file_printf("<property>");
+    {
+        /* These two properties are not symbols, so they won't be emitted
+           by emit_debug_information_for_predefined_symbol(). Do it
+           manually. */
+        debug_file_printf("<property>");
         debug_file_printf
             ("<identifier artificial=\"true\">inheritance class</identifier>");
         debug_file_printf("<value>2</value>");
@@ -2190,15 +2336,19 @@ extern void objects_begin_pass(void)
     else no_attributes = 0;
 
     no_objects = 0;
+    /* Setting the info for object zero is probably a relic of very old code, but we do it. */
     if (!glulx_mode) {
+        ensure_memory_list_available(&objectsz_memlist, 1);
         objectsz[0].parent = 0; objectsz[0].child = 0; objectsz[0].next = 0;
         no_individual_properties=72;
     }
     else {
+        ensure_memory_list_available(&objectsg_memlist, 1);
         objectsg[0].parent = 0; objectsg[0].child = 0; objectsg[0].next = 0;
         no_individual_properties = INDIV_PROP_START+8;
     }
     no_classes = 0;
+    current_classname_symbol = 0;
 
     no_embedded_routines = 0;
 
@@ -2211,66 +2361,75 @@ extern void objects_allocate_arrays(void)
     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");
+    commonprops = my_calloc(sizeof(commonpropinfo), INDIV_PROP_START,
+                                "common property info");
 
-    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");
+    initialise_memory_list(&class_info_memlist,
+        sizeof(classinfo), 64, (void**)&class_info,
+        "class info");
+    initialise_memory_list(&classes_to_inherit_from_memlist,
+        sizeof(int),       64, (void**)&classes_to_inherit_from,
+        "inherited classes list");
 
-    properties_table      = my_malloc(MAX_PROP_TABLE_SIZE,"properties table");
-    individuals_table     = my_malloc(MAX_INDIV_PROP_TABLE_SIZE,
-                                "individual properties table");
+    initialise_memory_list(&properties_table_memlist,
+        sizeof(uchar), 10000, (void**)&properties_table,
+        "properties table");
+    initialise_memory_list(&individuals_table_memlist,
+        sizeof(uchar), 10000, (void**)&individuals_table,
+        "individual properties table");
 
     defined_this_segment_size = 128;
     defined_this_segment  = my_calloc(sizeof(int), defined_this_segment_size,
                                 "defined this segment table");
 
+    initialise_memory_list(&current_object_name,
+        sizeof(char), 32, NULL,
+        "object name currently being defined");
+    initialise_memory_list(&embedded_function_name,
+        sizeof(char), 32, NULL,
+        "temporary storage for inline function name");
+    
     if (!glulx_mode) {
-      objectsz            = my_calloc(sizeof(objecttz), MAX_OBJECTS, 
-                                "z-objects");
+      initialise_memory_list(&objectsz_memlist,
+          sizeof(objecttz), 256, (void**)&objectsz,
+          "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");
+      initialise_memory_list(&objectsg_memlist,
+          sizeof(objecttg), 256, (void**)&objectsg,
+          "g-objects");
+      initialise_memory_list(&objectatts_memlist,
+          NUM_ATTR_BYTES, 256, (void**)&objectatts,
+          "g-attributes");
+      initialise_memory_list(&full_object_g.props_memlist,
+          sizeof(propg), 64, (void**)&full_object_g.props,
+          "object property list");
+      initialise_memory_list(&full_object_g.propdata_memlist,
+          sizeof(assembly_operand), 1024, (void**)&full_object_g.propdata,
+          "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(&commonprops, "common property info");
+    
+    deallocate_memory_list(&current_object_name);
+    deallocate_memory_list(&embedded_function_name);
+    deallocate_memory_list(&objectsz_memlist);
+    deallocate_memory_list(&objectsg_memlist);
+    deallocate_memory_list(&objectatts_memlist);
+    deallocate_memory_list(&class_info_memlist);
+    deallocate_memory_list(&classes_to_inherit_from_memlist);
 
-    my_free(&properties_table, "properties table");
-    my_free(&individuals_table,"individual properties table");
+    deallocate_memory_list(&properties_table_memlist);
+    deallocate_memory_list(&individuals_table_memlist);
 
     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");
+        deallocate_memory_list(&full_object_g.props_memlist);
+        deallocate_memory_list(&full_object_g.propdata_memlist);
     }
     
 }
index b569fb26cd933e66974fd551aaa5b7e4460b8766..ea495505cf70bdef2b8f4bce014f0a4b1ff24dfd 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "states" :  Statement translator                                        */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -15,7 +15,7 @@
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -259,17 +259,17 @@ extern int parse_label(void)
     get_next_token();
 
     if ((token_type == SYMBOL_TT) &&
-        (stypes[token_value] == LABEL_T))
-    {   sflags[token_value] |= USED_SFLAG;
-        return(svals[token_value]);
+        (symbols[token_value].type == LABEL_T))
+    {   symbols[token_value].flags |= USED_SFLAG;
+        return(symbols[token_value].value);
     }
 
-    if ((token_type == SYMBOL_TT) && (sflags[token_value] & UNKNOWN_SFLAG))
+    if ((token_type == SYMBOL_TT) && (symbols[token_value].flags & UNKNOWN_SFLAG))
     {   assign_symbol(token_value, next_label, LABEL_T);
         define_symbol_label(token_value);
         next_label++;
-        sflags[token_value] |= CHANGE_SFLAG + USED_SFLAG;
-        return(svals[token_value]);
+        symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG;
+        return(symbols[token_value].value);
     }
 
     ebf_error("label name", token_text);
@@ -429,19 +429,21 @@ static void parse_print_z(int finally_return)
                           break;
 
                         case SYMBOL_TT:
-                          if (sflags[token_value] & UNKNOWN_SFLAG)
+                          if (symbols[token_value].flags & UNKNOWN_SFLAG)
                           {   INITAOT(&AO, LONG_CONSTANT_OT);
                               AO.value = token_value;
                               AO.marker = SYMBOL_MV;
+                              AO.symindex = token_value;
                           }
                           else
                           {   INITAOT(&AO, LONG_CONSTANT_OT);
-                              AO.value = svals[token_value];
+                              AO.value = symbols[token_value].value;
                               AO.marker = IROUTINE_MV;
-                              if (stypes[token_value] != ROUTINE_T)
+                              AO.symindex = token_value;
+                              if (symbols[token_value].type != ROUTINE_T)
                                 ebf_error("printing routine name", token_text);
                           }
-                          sflags[token_value] |= USED_SFLAG;
+                          symbols[token_value].flags |= USED_SFLAG;
 
                           PrintByRoutine:
 
@@ -664,19 +666,21 @@ static void parse_print_g(int finally_return)
                           break;
 
                         case SYMBOL_TT:
-                          if (sflags[token_value] & UNKNOWN_SFLAG)
+                          if (symbols[token_value].flags & UNKNOWN_SFLAG)
                           {   INITAOT(&AO, CONSTANT_OT);
                               AO.value = token_value;
                               AO.marker = SYMBOL_MV;
+                              AO.symindex = token_value;
                           }
                           else
                           {   INITAOT(&AO, CONSTANT_OT);
-                              AO.value = svals[token_value];
+                              AO.value = symbols[token_value].value;
                               AO.marker = IROUTINE_MV;
-                              if (stypes[token_value] != ROUTINE_T)
+                              AO.symindex = token_value;
+                              if (symbols[token_value].type != ROUTINE_T)
                                 ebf_error("printing routine name", token_text);
                           }
-                          sflags[token_value] |= USED_SFLAG;
+                          symbols[token_value].flags |= USED_SFLAG;
 
                           PrintByRoutine:
 
@@ -734,57 +738,73 @@ static void parse_print_g(int finally_return)
     }
 }
 
-static void parse_statement_z(int break_label, int continue_label)
-{   int ln, ln2, ln3, ln4, flag;
-    assembly_operand AO, AO2, AO3, AO4;
-    debug_location spare_debug_location1, spare_debug_location2;
-
-    ASSERT_ZCODE();
-
-    if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
+/* Parse any number of ".Label;" lines before a statement.
+   Returns whether a statement can in fact follow. */
+static int parse_named_label_statements()
+{
+    while ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
     {   /*  That is, a full stop, signifying a label  */
 
         get_next_token();
-        if (token_type == SYMBOL_TT)
+        if (token_type != SYMBOL_TT)
         {
-            if (sflags[token_value] & UNKNOWN_SFLAG)
-            {   assign_symbol(token_value, next_label, LABEL_T);
-                sflags[token_value] |= USED_SFLAG;
-                assemble_label_no(next_label);
-                define_symbol_label(token_value);
-                next_label++;
+            ebf_error("label name", token_text);
+            return TRUE;
+        }
+
+        if (symbols[token_value].flags & UNKNOWN_SFLAG)
+        {   assign_symbol(token_value, next_label, LABEL_T);
+            symbols[token_value].flags |= USED_SFLAG;
+            assemble_label_no(next_label);
+            define_symbol_label(token_value);
+            next_label++;
+        }
+        else
+        {   if (symbols[token_value].type != LABEL_T) {
+                ebf_error("label name", token_text);
+                return TRUE;
             }
-            else
-            {   if (stypes[token_value] != LABEL_T) goto LabelError;
-                if (sflags[token_value] & CHANGE_SFLAG)
-                {   sflags[token_value] &= (~(CHANGE_SFLAG));
-                    assemble_label_no(svals[token_value]);
-                    define_symbol_label(token_value);
-                }
-                else error_named("Duplicate definition of label:", token_text);
+            if (symbols[token_value].flags & CHANGE_SFLAG)
+            {   symbols[token_value].flags &= (~(CHANGE_SFLAG));
+                assemble_label_no(symbols[token_value].value);
+                define_symbol_label(token_value);
             }
+            else error_named("Duplicate definition of label:", token_text);
+        }
 
-            get_next_token();
-            if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
-            {   ebf_error("';'", token_text);
-                put_token_back(); return;
-            }
+        get_next_token();
+        if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
+        {   ebf_error("';'", token_text);
+            put_token_back(); return FALSE;
+        }
 
-            /*  Interesting point of Inform grammar: a statement can only
-                consist solely of a label when it is immediately followed
-                by a "}".                                                    */
+        /*  Interesting point of Inform grammar: a statement can only
+            consist solely of a label when it is immediately followed
+            by a "}".                                                    */
 
-            get_next_token();
-            if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
-            {   put_token_back(); return;
-            }
-            statement_debug_location = get_token_location();
-            parse_statement(break_label, continue_label);
-            return;
+        get_next_token();
+        if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
+        {   put_token_back(); return FALSE;
         }
-        LabelError: ebf_error("label name", token_text);
+        /* The following line prevents labels from influencing the positions
+           of sequence points. */
+        statement_debug_location = get_token_location();
+        
+        /* Another label might follow */
     }
 
+    /* On with the statement */
+    return TRUE;
+}
+
+static void parse_statement_z(int break_label, int continue_label)
+{   int ln, ln2, ln3, ln4, flag;
+    int pre_unreach, labelexists;
+    assembly_operand AO, AO2, AO3, AO4;
+    debug_location spare_debug_location1, spare_debug_location2;
+
+    ASSERT_ZCODE();
+
     if ((token_type == SEP_TT) && (token_value == HASH_SEP))
     {   parse_directive(TRUE);
         parse_statement(break_label, continue_label); return;
@@ -901,7 +921,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  get_next_token();
                  if ((token_type == STATEMENT_TT)
                      && (token_value == UNTIL_CODE))
-                 {   assemble_label_no(ln2);
+                 {   assemble_forward_label_no(ln2);
                      match_open_bracket();
                      AO = parse_expression(CONDITION_CONTEXT);
                      match_close_bracket();
@@ -909,7 +929,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  }
                  else error("'do' without matching 'until'");
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  break;
 
     /*  -------------------------------------------------------------------- */
@@ -997,7 +1017,7 @@ static void parse_statement_z(int break_label, int continue_label)
                              sequence_point_follows = FALSE;
                              if (!execution_never_reaches_here)
                                  assemblez_jump(ln);
-                             assemble_label_no(ln2);
+                             assemble_forward_label_no(ln2);
                              return;
                          }
                          goto ParseUpdate;
@@ -1106,7 +1126,7 @@ static void parse_statement_z(int break_label, int continue_label)
                      }
                  }
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1116,6 +1136,7 @@ static void parse_statement_z(int break_label, int continue_label)
         case GIVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                           QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
                  if ((AO.type == VARIABLE_OT) && (AO.value == 0))
                  {   INITAOTV(&AO, SHORT_CONSTANT_OT, 252);
                      if (version_number != 6) assemblez_1(pull_zc, AO);
@@ -1130,15 +1151,12 @@ static void parse_statement_z(int break_label, int continue_label)
                      if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
                          ln = clear_attr_zc;
                      else
-                     {   if ((token_type == SYMBOL_TT)
-                             && (stypes[token_value] != ATTRIBUTE_T))
-                           warning_named("This is not a declared Attribute:",
-                             token_text);
-                         ln = set_attr_zc;
+                     {   ln = set_attr_zc;
                          put_token_back();
                      }
                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1);
+                     check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
                      if (runtime_error_checking_switch)
                      {   ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
                          if (version_number >= 5)
@@ -1159,8 +1177,9 @@ static void parse_statement_z(int break_label, int continue_label)
     /*  -------------------------------------------------------------------- */
 
         case IF_CODE:
-                 flag = FALSE;
+                 flag = FALSE; /* set if there's an "else" */
                  ln2 = 0;
+                 pre_unreach = execution_never_reaches_here;
 
                  match_open_bracket();
                  AO = parse_expression(CONDITION_CONTEXT);
@@ -1178,8 +1197,17 @@ static void parse_statement_z(int break_label, int continue_label)
                      ln = next_label++;
                  }
 
+                 /* The condition */
                  code_generate(AO, CONDITION_CONTEXT, ln);
 
+                 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
+                     /* If the condition never falls through to here, then
+                        it was an "if (0)" test. Our convention is to skip
+                        the "not reached" warnings for this case. */
+                     execution_never_reaches_here |= EXECSTATE_NOWARN;
+                 }
+
+                 /* The "if" block */
                  if (ln >= 0) parse_code_block(break_label, continue_label, 0);
                  else
                  {   get_next_token();
@@ -1212,13 +1240,46 @@ static void parse_statement_z(int break_label, int continue_label)
                  }
                  else put_token_back();
 
-                 if (ln >= 0) assemble_label_no(ln);
+                 /* The "else" label (or end of statement, if there is no "else") */
+                 labelexists = FALSE;
+                 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
 
                  if (flag)
-                 {   parse_code_block(break_label, continue_label, 0);
-                     if (ln >= 0) assemble_label_no(ln2);
-                 }
+                 {
+                     /* If labelexists is false, then we started with
+                        "if (1)". In this case, we don't want a "not
+                        reached" warning on the "else" block. We
+                        temporarily disable the NOWARN flag, and restore it
+                        afterwards. */
+                     int saved_unreach = 0;
+                     if (execution_never_reaches_here && !labelexists) {
+                         saved_unreach = execution_never_reaches_here;
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+
+                     /* The "else" block */
+                     parse_code_block(break_label, continue_label, 0);
 
+                     if (execution_never_reaches_here && !labelexists) {
+                         if (saved_unreach & EXECSTATE_NOWARN)
+                             execution_never_reaches_here |= EXECSTATE_NOWARN;
+                         else
+                             execution_never_reaches_here &= ~EXECSTATE_NOWARN;
+                     }
+
+                     /* The post-"else" label */
+                     if (ln >= 0) assemble_forward_label_no(ln2);
+                 }
+                 else
+                 {
+                     /* There was no "else". If we're unreachable, then the
+                        statement returned unconditionally, which means 
+                        "if (1) return". Skip warnings. */
+                     if (!pre_unreach && execution_never_reaches_here) {
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+                 }
+                         
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1271,6 +1332,8 @@ static void parse_statement_z(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  AO = code_generate(AO, QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
+                 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                  {   if (version_number >= 5)
                          assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
@@ -1303,8 +1366,8 @@ static void parse_statement_z(int break_label, int continue_label)
                      AO.value = token_value;
                  else
                  if ((token_type == SYMBOL_TT) &&
-                     (stypes[token_value] == GLOBAL_VARIABLE_T))
-                     AO.value = svals[token_value];
+                     (symbols[token_value].type == GLOBAL_VARIABLE_T))
+                     AO.value = symbols[token_value].value;
                  else
                  {   ebf_error("'objectloop' variable", token_text);
                      panic_mode_error_recovery(); break;
@@ -1490,6 +1553,7 @@ static void parse_statement_z(int break_label, int continue_label)
         case REMOVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                  {   if (version_number >= 5)
                          assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR),
@@ -1599,13 +1663,11 @@ static void parse_statement_z(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  if (is_constant_ot(AO2.type) && AO2.marker == 0) {
-                     if (AO2.value >= 96)
-                     {   error("Z-machine dynamic strings are limited to 96");
+                     /* Compile-time check */
+                     if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
+                         error_max_dynamic_strings(AO2.value);
                          AO2.value = 0;
                      }
-                     if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
-                         memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS);
-                     }
                  }
                  get_next_token();
                  if (token_type == DQ_TT)
@@ -1673,7 +1735,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  assemblez_store(AO2, AO);
 
                  parse_code_block(ln = next_label++, continue_label, 1);
-                 assemble_label_no(ln);
+                 assemble_forward_label_no(ln);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1691,7 +1753,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  parse_code_block(ln2, ln, 0);
                  sequence_point_follows = FALSE;
                  assemblez_jump(ln);
-                 assemble_label_no(ln2);
+                 assemble_forward_label_no(ln2);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1716,57 +1778,12 @@ static void parse_statement_z(int break_label, int continue_label)
 
 static void parse_statement_g(int break_label, int continue_label)
 {   int ln, ln2, ln3, ln4, flag, onstack;
+    int pre_unreach, labelexists;
     assembly_operand AO, AO2, AO3, AO4;
     debug_location spare_debug_location1, spare_debug_location2;
 
     ASSERT_GLULX();
 
-    if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
-    {   /*  That is, a full stop, signifying a label  */
-
-        get_next_token();
-        if (token_type == SYMBOL_TT)
-        {
-            if (sflags[token_value] & UNKNOWN_SFLAG)
-            {   assign_symbol(token_value, next_label, LABEL_T);
-                sflags[token_value] |= USED_SFLAG;
-                assemble_label_no(next_label);
-                define_symbol_label(token_value);
-                next_label++;
-            }
-            else
-            {   if (stypes[token_value] != LABEL_T) goto LabelError;
-                if (sflags[token_value] & CHANGE_SFLAG)
-                {   sflags[token_value] &= (~(CHANGE_SFLAG));
-                    assemble_label_no(svals[token_value]);
-                    define_symbol_label(token_value);
-                }
-                else error_named("Duplicate definition of label:", token_text);
-            }
-
-            get_next_token();
-            if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
-            {   ebf_error("';'", token_text);
-                put_token_back(); return;
-            }
-
-            /*  Interesting point of Inform grammar: a statement can only
-                consist solely of a label when it is immediately followed
-                by a "}".                                                    */
-
-            get_next_token();
-            if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
-            {   put_token_back(); return;
-            }
-            /* The following line prevents labels from influencing the positions
-               of sequence points. */
-            statement_debug_location = get_token_location();
-            parse_statement(break_label, continue_label);
-            return;
-        }
-        LabelError: ebf_error("label name", token_text);
-    }
-
     if ((token_type == SEP_TT) && (token_value == HASH_SEP))
     {   parse_directive(TRUE);
         parse_statement(break_label, continue_label); return;
@@ -1880,7 +1897,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  get_next_token();
                  if ((token_type == STATEMENT_TT)
                      && (token_value == UNTIL_CODE))
-                 {   assemble_label_no(ln2);
+                 {   assemble_forward_label_no(ln2);
                      match_open_bracket();
                      AO = parse_expression(CONDITION_CONTEXT);
                      match_close_bracket();
@@ -1888,7 +1905,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  }
                  else error("'do' without matching 'until'");
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  break;
 
     /*  -------------------------------------------------------------------- */
@@ -1958,7 +1975,7 @@ static void parse_statement_g(int break_label, int continue_label)
                              sequence_point_follows = FALSE;
                              if (!execution_never_reaches_here)
                                  assembleg_jump(ln);
-                             assemble_label_no(ln2);
+                             assemble_forward_label_no(ln2);
                              return;
                          }
                          goto ParseUpdate;
@@ -2071,7 +2088,7 @@ static void parse_statement_g(int break_label, int continue_label)
                      }
                  }
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2081,6 +2098,7 @@ static void parse_statement_g(int break_label, int continue_label)
         case GIVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                           QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
                  if ((AO.type == LOCALVAR_OT) && (AO.value == 0))
                      onstack = TRUE;
                  else
@@ -2098,15 +2116,12 @@ static void parse_statement_g(int break_label, int continue_label)
                      if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
                          ln = 0;
                      else
-                     {   if ((token_type == SYMBOL_TT)
-                             && (stypes[token_value] != ATTRIBUTE_T))
-                           warning_named("This is not a declared Attribute:",
-                             token_text);
-                         ln = 1;
+                     {   ln = 1;
                          put_token_back();
                      }
                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1);
+                     check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
                      if (runtime_error_checking_switch && (!veneer_mode))
                      {   ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR);
                          if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
@@ -2153,8 +2168,9 @@ static void parse_statement_g(int break_label, int continue_label)
     /*  -------------------------------------------------------------------- */
 
         case IF_CODE:
-                 flag = FALSE;
+                 flag = FALSE; /* set if there's an "else" */
                  ln2 = 0;
+                 pre_unreach = execution_never_reaches_here;
 
                  match_open_bracket();
                  AO = parse_expression(CONDITION_CONTEXT);
@@ -2172,8 +2188,17 @@ static void parse_statement_g(int break_label, int continue_label)
                      ln = next_label++;
                  }
 
+                 /* The condition */
                  code_generate(AO, CONDITION_CONTEXT, ln);
 
+                 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
+                     /* If the condition never falls through to here, then
+                        it was an "if (0)" test. Our convention is to skip
+                        the "not reached" warnings for this case. */
+                     execution_never_reaches_here |= EXECSTATE_NOWARN;
+                 }
+
+                 /* The "if" block */
                  if (ln >= 0) parse_code_block(break_label, continue_label, 0);
                  else
                  {   get_next_token();
@@ -2206,11 +2231,44 @@ static void parse_statement_g(int break_label, int continue_label)
                  }
                  else put_token_back();
 
-                 if (ln >= 0) assemble_label_no(ln);
+                 /* The "else" label (or end of statement, if there is no "else") */
+                 labelexists = FALSE;
+                 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
 
                  if (flag)
-                 {   parse_code_block(break_label, continue_label, 0);
-                     if (ln >= 0) assemble_label_no(ln2);
+                 {
+                     /* If labelexists is false, then we started with
+                        "if (1)". In this case, we don't want a "not
+                        reached" warning on the "else" block. We
+                        temporarily disable the NOWARN flag, and restore it
+                        afterwards. */
+                     int saved_unreach = 0;
+                     if (execution_never_reaches_here && !labelexists) {
+                         saved_unreach = execution_never_reaches_here;
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+
+                     /* The "else" block */
+                     parse_code_block(break_label, continue_label, 0);
+
+                     if (execution_never_reaches_here && !labelexists) {
+                         if (saved_unreach & EXECSTATE_NOWARN)
+                             execution_never_reaches_here |= EXECSTATE_NOWARN;
+                         else
+                             execution_never_reaches_here &= ~EXECSTATE_NOWARN;
+                     }
+
+                     /* The post-"else" label */
+                     if (ln >= 0) assemble_forward_label_no(ln2);
+                 }
+                 else
+                 {
+                     /* There was no "else". If we're unreachable, then the
+                        statement returned unconditionally, which means 
+                        "if (1) return". Skip warnings. */
+                     if (!pre_unreach && execution_never_reaches_here) {
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
                  }
 
                  return;
@@ -2291,6 +2349,8 @@ static void parse_statement_g(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  AO = code_generate(AO, QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
+                 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                      assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2,
                          zero_operand);
@@ -2320,8 +2380,8 @@ static void parse_statement_g(int break_label, int continue_label)
                      INITAOTV(&AO, LOCALVAR_OT, token_value);
                  }
                  else if ((token_type == SYMBOL_TT) &&
-                   (stypes[token_value] == GLOBAL_VARIABLE_T)) {
-                     INITAOTV(&AO, GLOBALVAR_OT, svals[token_value]);
+                   (symbols[token_value].type == GLOBAL_VARIABLE_T)) {
+                     INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
                  }
                  else {
                      ebf_error("'objectloop' variable", token_text);
@@ -2423,7 +2483,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  sequence_point_follows = TRUE;
                  ln = symbol_index("Class", -1);
                  INITAOT(&AO2, CONSTANT_OT);
-                 AO2.value = svals[ln];
+                 AO2.value = symbols[ln].value;
                  AO2.marker = OBJECT_MV;
                  assembleg_store(AO, AO2);
 
@@ -2473,6 +2533,7 @@ static void parse_statement_g(int break_label, int continue_label)
         case REMOVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                      assembleg_call_1(veneer_routine(RT__ChR_VR), AO,
                          zero_operand);
@@ -2543,8 +2604,9 @@ static void parse_statement_g(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  if (is_constant_ot(AO2.type) && AO2.marker == 0) {
+                     /* Compile-time check */
                      if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
-                         memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS);
+                         error_max_dynamic_strings(AO2.value);
                      }
                  }
                  get_next_token();
@@ -2629,7 +2691,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  assembleg_store(temp_var1, AO); 
 
                  parse_code_block(ln = next_label++, continue_label, 1);
-                 assemble_label_no(ln);
+                 assemble_forward_label_no(ln);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2647,7 +2709,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  parse_code_block(ln2, ln, 0);
                  sequence_point_follows = FALSE;
                  assembleg_jump(ln);
-                 assemble_label_no(ln2);
+                 assemble_forward_label_no(ln2);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2681,10 +2743,26 @@ static void parse_statement_g(int break_label, int continue_label)
 
 extern void parse_statement(int break_label, int continue_label)
 {
-  if (!glulx_mode)
-    parse_statement_z(break_label, continue_label);
-  else
-    parse_statement_g(break_label, continue_label);
+    int res;
+    int saved_entire_flag;
+    
+    res = parse_named_label_statements();
+    if (!res)
+        return;
+
+    saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
+    if (execution_never_reaches_here)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    if (!glulx_mode)
+        parse_statement_z(break_label, continue_label);
+    else
+        parse_statement_g(break_label, continue_label);
+
+    if (saved_entire_flag)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    else
+        execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
 }
 
 /* ========================================================================= */
index 6d736e940832cf1893b288d8417811a57e61fd24..8f2a09e52c90a4bd563e73bec844235caa60cb5f 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "symbols" :  The symbols table; creating stock of reserved words        */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -15,7 +15,7 @@
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -30,24 +30,32 @@ int no_symbols;                        /* Total number of symbols defined    */
 int no_named_constants;                         /* Copied into story file    */
 
 /* ------------------------------------------------------------------------- */
-/*   Plus six arrays.  Each symbol has its own index n (an int32) and        */
+/*   Plus an array of symbolinfo.  Each symbol has its own index n (an       */
+/*   int32) in the array. The struct there contains:                         */
 /*                                                                           */
-/*       svals[n]   is its value (must be 32 bits wide, i.e. an int32, tho'  */
-/*                  it is used to hold an unsigned 16 bit Z-machine value)   */
-/*       sflags[n]  holds flags (see "header.h" for a list)                  */
-/*       stypes[n]  is the "type", distinguishing between the data type of   */
+/*       value   is its value. In Z-code, this holds both the 16-bit value   */
+/*                  and the 16-bit backpatch marker, so it is an int32.      */
+/*       marker   is the backpatch marker in Glulx.                          */
+/*       flags  holds flags (see "header.h" for a list of ?_SFLAGS)          */
+/*       type  is the "type", distinguishing between the data type of        */
 /*                  different kinds of constants/variables.                  */
-/*                  (See the "typename()" below.)                            */
-/*       symbs[n]   (needs to be cast to (char *) to be used) is the name    */
-/*                  of the symbol, in the same case form as when created.    */
-/*       slines[n]  is the source line on which the symbol value was first   */
+/*                  (A ?_T constant; see the "typename()" below.)            */
+/*       name   is the name of the symbol, in the same case form as          */
+/*                  when created.                                            */
+/*       line  is the source line on which the symbol value was first        */
 /*                  assigned                                                 */
-/*       symbol_debug_backpatch_positions[n]                                 */
+/*       next_entry  is the forward link in the symbol hash table. (See      */
+/*                  start_of_list, below.)                                   */
+/*                                                                           */
+/*   When generating a debug file (-k switch), we also allocate an array     */
+/*   of symboldebuginfo, which contains:                                     */
+/*                                                                           */
+/*       backpatch_pos                                                       */
 /*                  is a file position in the debug information file where   */
 /*                  the symbol's value should be written after backpatching, */
 /*                  or else the null position if the value was known and     */
 /*                  written beforehand                                       */
-/*       replacement_debug_backpatch_positions[n]                            */
+/*       replacement_backpatch_pos                                           */
 /*                  is a file position in the debug information file where   */
 /*                  the symbol's name can be erased if it is replaced, or    */
 /*                  else null if the name will never need to be replaced     */
@@ -55,39 +63,28 @@ int no_named_constants;                         /* Copied into story file    */
 /*   Comparison is case insensitive.                                         */
 /*   Note that local variable names are not entered into the symbols table,  */
 /*   as their numbers and scope are too limited for this to be efficient.    */
-/* ------------------------------------------------------------------------- */
-/*   Caveat editor: some array types are set up to work even on machines     */
-/*   where sizeof(int32 *) differs from, e.g., sizeof(char *): so do not     */
-/*   alter the types unless you understand what is going on!                 */
 /* ------------------------------------------------------------------------- */
 
-  int32  **symbs;
-  int32  *svals;
-  int    *smarks;            /* Glulx-only */
-  brief_location  *slines;
-  int    *sflags;
-#ifdef VAX
-  char   *stypes;            /* In VAX C, insanely, "signed char" is illegal */
-#else
-  signed char *stypes;
-#endif
-  maybe_file_position  *symbol_debug_backpatch_positions;
-  maybe_file_position  *replacement_debug_backpatch_positions;
+symbolinfo *symbols;                           /* Allocated up to no_symbols */
+static memory_list symbols_memlist;
+symboldebuginfo *symbol_debug_info;            /* Allocated up to no_symbols */
+static memory_list symbol_debug_info_memlist;
 
 /* ------------------------------------------------------------------------- */
 /*   Memory to hold the text of symbol names: note that this memory is       */
 /*   allocated as needed in chunks of size SYMBOLS_CHUNK_SIZE.               */
 /* ------------------------------------------------------------------------- */
 
-#define MAX_SYMBOL_CHUNKS (100)
+#define SYMBOLS_CHUNK_SIZE (4096)
 
-static uchar *symbols_free_space,       /* Next byte free to hold new names  */
+static char *symbols_free_space,        /* Next byte free to hold new names  */
            *symbols_ceiling;            /* Pointer to the end of the current
                                            allocation of memory for names    */
 
 static char** symbol_name_space_chunks; /* For chunks of memory used to hold
                                            the name strings of symbols       */
 static int no_symbol_name_space_chunks;
+static memory_list symbol_name_space_chunks_memlist;
 
 /* Symbol replacements (used by the "Replace X Y" directive). */
 
@@ -131,8 +128,8 @@ static int symbol_definitions_size = 0; /* calloced size */
 /*   idea to choose HASH_TAB_SIZE as large as conveniently possible.         */
 /* ------------------------------------------------------------------------- */
 
-static int   *next_entry;
-static int32 *start_of_list;
+static int32 *start_of_list; /* Allocated array of size HASH_TAB_SIZE */
+/* The next_entry field is part of the symbolinfo struct. */
 
 /* ------------------------------------------------------------------------- */
 /*   Initialisation.                                                         */
@@ -187,7 +184,8 @@ extern int strcmpcis(char *p, char *q)
 /* ------------------------------------------------------------------------- */
 
 extern void add_config_symbol_definition(char *symbol, int32 value)
-{
+{   char *str;
+
     if (symbol_definitions_count == symbol_definitions_size) {
         int oldsize = symbol_definitions_size;
         if (symbol_definitions_size == 0) 
@@ -198,7 +196,7 @@ extern void add_config_symbol_definition(char *symbol, int32 value)
             symbol_definitions_size, "symbol definition table");
     }
 
-    char *str = my_malloc(strlen(symbol)+1, "symbol name");
+    str = my_malloc(strlen(symbol)+1, "symbol name");
     strcpy(str, symbol);
     
     symbol_definitions[symbol_definitions_count].symbol = str;
@@ -210,17 +208,48 @@ extern void add_config_symbol_definition(char *symbol, int32 value)
 /*   Symbol finding, creating, and removing.                                 */
 /* ------------------------------------------------------------------------- */
 
+extern int get_symbol_index(char *p)
+{
+    /*  Return the index in the symbols array of symbol "p", or -1
+        if it isn't there. Does not create a new symbol or mark the
+        symbol as used. */
+
+    int32 new_entry, this;
+    char *r;
+    int hashcode = hash_code_from_string(p);
+
+    this = start_of_list[hashcode];
+
+    do
+    {   if (this == -1) break;
+
+        r = symbols[this].name;
+        new_entry = strcmpcis(r, p);
+        if (new_entry == 0) 
+        {
+            return this;
+        }
+        if (new_entry > 0) break;
+
+        this = symbols[this].next_entry;
+    } while (this != -1);
+
+    return -1;
+}
+
 extern int symbol_index(char *p, int hashcode)
 {
-    /*  Return the index in the symbs/svals/sflags/stypes/... arrays of symbol
-        "p", creating a new symbol with that name if it isn't already there.
+    /*  Return the index in the symbols array of symbol "p", creating a
+        new symbol with that name if it isn't already there.
 
         New symbols are created with flag UNKNOWN_SFLAG, value 0x100
         (a 2-byte quantity in Z-machine terms) and type CONSTANT_T.
 
         The string "p" is undamaged.                                         */
 
-    int32 new_entry, this, last; char *r;
+    int32 new_entry, this, last;
+    char *r;
+    int len;    
 
     if (hashcode == -1) hashcode = hash_code_from_string(p);
 
@@ -229,7 +258,7 @@ extern int symbol_index(char *p, int hashcode)
     do
     {   if (this == -1) break;
 
-        r = (char *)symbs[this];
+        r = symbols[this].name;
         new_entry = strcmpcis(r, p);
         if (new_entry == 0) 
         {
@@ -240,51 +269,55 @@ extern int symbol_index(char *p, int hashcode)
         if (new_entry > 0) break;
 
         last = this;
-        this = next_entry[this];
+        this = symbols[this].next_entry;
     } while (this != -1);
 
-    if (no_symbols >= MAX_SYMBOLS)
-        memoryerror("MAX_SYMBOLS", MAX_SYMBOLS);
+    if (symdef_trace_setting)
+        printf("Encountered symbol %d '%s'\n", no_symbols, p);
+    
+    ensure_memory_list_available(&symbols_memlist, no_symbols+1);
+    if (debugfile_switch)
+        ensure_memory_list_available(&symbol_debug_info_memlist, no_symbols+1);
 
     if (last == -1)
-    {   next_entry[no_symbols]=start_of_list[hashcode];
+    {   symbols[no_symbols].next_entry=start_of_list[hashcode];
         start_of_list[hashcode]=no_symbols;
     }
     else
-    {   next_entry[no_symbols]=this;
-        next_entry[last]=no_symbols;
+    {   symbols[no_symbols].next_entry=this;
+        symbols[last].next_entry=no_symbols;
     }
 
-    if (symbols_free_space+strlen(p)+1 >= symbols_ceiling)
+    len = strlen(p);
+    if (symbols_free_space+len+1 >= symbols_ceiling)
     {   symbols_free_space
             = my_malloc(SYMBOLS_CHUNK_SIZE, "symbol names chunk");
         symbols_ceiling = symbols_free_space + SYMBOLS_CHUNK_SIZE;
-        /* If we've passed MAX_SYMBOL_CHUNKS chunks, we print an error
-           message telling the user to increase SYMBOLS_CHUNK_SIZE.
-           That is the correct cure, even though the error comes out
-           worded inaccurately. */
-        if (no_symbol_name_space_chunks >= MAX_SYMBOL_CHUNKS)
-            memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE);
+        ensure_memory_list_available(&symbol_name_space_chunks_memlist, no_symbol_name_space_chunks+1);
         symbol_name_space_chunks[no_symbol_name_space_chunks++]
-            = (char *) symbols_free_space;
-        if (symbols_free_space+strlen(p)+1 >= symbols_ceiling)
-            memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE);
+            = symbols_free_space;
+        if (symbols_free_space+len+1 >= symbols_ceiling)
+        {
+            /* This should be impossible, since SYMBOLS_CHUNK_SIZE > MAX_IDENTIFIER_LENGTH. */
+            fatalerror("Symbol exceeds the maximum possible length");
+        }
     }
 
-    strcpy((char *) symbols_free_space, p);
-    symbs[no_symbols] = (int32 *) symbols_free_space;
-    symbols_free_space += strlen((char *)symbols_free_space) + 1;
+    strcpy(symbols_free_space, p);
+    symbols[no_symbols].name   = symbols_free_space;
+    symbols_free_space += (len+1);
 
-    svals[no_symbols]   =  0x100; /* ###-wrong? Would this fix the
+    symbols[no_symbols].value   =  0x100; /* ###-wrong? Would this fix the
                                      unbound-symbol-causes-asm-error? */
-    sflags[no_symbols]  =  UNKNOWN_SFLAG;
-    stypes[no_symbols]  =  CONSTANT_T;
-    slines[no_symbols]  =  get_brief_location(&ErrorReport);
+    symbols[no_symbols].flags  =  UNKNOWN_SFLAG;
+    symbols[no_symbols].marker =  0;
+    symbols[no_symbols].type  =  CONSTANT_T;
+    symbols[no_symbols].line  =  get_brief_location(&ErrorReport);
     if (debugfile_switch)
     {   nullify_debug_file_position
-            (&symbol_debug_backpatch_positions[no_symbols]);
+            (&symbol_debug_info[no_symbols].backpatch_pos);
         nullify_debug_file_position
-            (&replacement_debug_backpatch_positions[no_symbols]);
+            (&symbol_debug_info[no_symbols].replacement_backpatch_pos);
     }
 
     if (track_unused_routines)
@@ -300,19 +333,19 @@ extern void end_symbol_scope(int k)
     */
 
     int j;
-    j = hash_code_from_string((char *) symbs[k]);
+    j = hash_code_from_string(symbols[k].name);
     if (start_of_list[j] == k)
-    {   start_of_list[j] = next_entry[k];
+    {   start_of_list[j] = symbols[k].next_entry;
         return;
     }
     j = start_of_list[j];
     while (j != -1)
     {
-        if (next_entry[j] == k)
-        {   next_entry[j] = next_entry[k];
+        if (symbols[j].next_entry == k)
+        {   symbols[j].next_entry = symbols[k].next_entry;
             return;
         }
-        j = next_entry[j];
+        j = symbols[j].next_entry;
     }
 }
 
@@ -340,6 +373,10 @@ extern char *typename(int type)
         case OBJECT_T:              return("Object");
         case CLASS_T:               return("Class");
         case FAKE_ACTION_T:         return("Fake action");
+            
+        /*  These are not symbol types, but they get printed in errors. */
+        case STRING_REQ_T:          return("String");
+        case DICT_WORD_REQ_T:       return("Dictionary word");
 
         default:                   return("(Unknown type)");
     }
@@ -347,40 +384,140 @@ extern char *typename(int type)
 
 static void describe_flags(int flags)
 {   if (flags & UNKNOWN_SFLAG)  printf("(?) ");
-    if (flags & USED_SFLAG)     printf("(used) ");
     if (flags & REPLACE_SFLAG)  printf("(Replaced) ");
+    if (flags & USED_SFLAG)     printf("(used) ");
     if (flags & DEFCON_SFLAG)   printf("(Defaulted) ");
     if (flags & STUB_SFLAG)     printf("(Stubbed) ");
-    if (flags & CHANGE_SFLAG)   printf("(value will change) ");
     if (flags & IMPORT_SFLAG)   printf("(Imported) ");
     if (flags & EXPORT_SFLAG)   printf("(Exported) ");
+    if (flags & ALIASED_SFLAG)  printf("(aliased) ");
+    if (flags & CHANGE_SFLAG)   printf("(value will change) ");
     if (flags & SYSTEM_SFLAG)   printf("(System) ");
     if (flags & INSF_SFLAG)     printf("(created in sys file) ");
     if (flags & UERROR_SFLAG)   printf("('Unknown' error issued) ");
-    if (flags & ALIASED_SFLAG)  printf("(aliased) ");
     if (flags & ACTION_SFLAG)   printf("(Action name) ");
     if (flags & REDEFINABLE_SFLAG) printf("(Redefinable) ");
+    if (flags & STAR_SFLAG)     printf("(*) ");
 }
 
 extern void describe_symbol(int k)
 {   printf("%4d  %-16s  %2d:%04d  %04x  %s  ",
-        k, (char *) (symbs[k]), 
-        (int)(slines[k].file_index),
-        (int)(slines[k].line_number),
-        svals[k], typename(stypes[k]));
-    describe_flags(sflags[k]);
+        k, (symbols[k].name), 
+        (int)(symbols[k].line.file_index),
+        (int)(symbols[k].line.line_number),
+        symbols[k].value, typename(symbols[k].type));
+    describe_flags(symbols[k].flags);
 }
 
 extern void list_symbols(int level)
 {   int k;
     for (k=0; k<no_symbols; k++)
-    {   if ((level==2) ||
-            ((sflags[k] & (SYSTEM_SFLAG + UNKNOWN_SFLAG + INSF_SFLAG)) == 0))
+    {   if ((level>=2) ||
+            ((symbols[k].flags & (SYSTEM_SFLAG + UNKNOWN_SFLAG + INSF_SFLAG)) == 0))
         {   describe_symbol(k); printf("\n");
         }
     }
 }
 
+/* Check that the operand is of the given symbol type (XXX_T). If wanttype2 is nonzero, that's a second allowable type.
+   Generate a warning if no match. */
+extern void check_warn_symbol_type(const assembly_operand *AO, int wanttype, int wanttype2, char *context)
+{
+    symbolinfo *sym;
+    int symtype;
+    
+    if (AO->symindex < 0)
+    {
+        /* This argument is not a symbol; it's a local variable, a literal, or a computed expression. */
+        /* We can recognize and type-check some literals. */
+        if (AO->marker == DWORD_MV) {
+            if (wanttype != DICT_WORD_REQ_T && wanttype2 != DICT_WORD_REQ_T)
+                symtype_warning(context, NULL, typename(DICT_WORD_REQ_T), typename(wanttype));
+        }
+        if (AO->marker == STRING_MV) {
+            if (wanttype != STRING_REQ_T && wanttype2 != STRING_REQ_T)
+                symtype_warning(context, NULL, typename(STRING_REQ_T), typename(wanttype));
+        }
+        return;
+    }
+    
+    sym = &symbols[AO->symindex];
+    symtype = sym->type;
+    
+    if (symtype == GLOBAL_VARIABLE_T)
+    {
+        /* A global variable could have any value. No way to generate a warning. */
+        return;
+    }
+    if (symtype == CONSTANT_T)
+    {
+        /* A constant could also have any value. This case also includes forward-declared constants (UNKNOWN_SFLAG). */
+        /* We try inferring its type by looking at the backpatch marker. Sadly, this only works for objects. (And not in Z-code, where object values are not backpatched.) */
+        if (sym->marker == OBJECT_MV) {
+            /* Continue with inferred type. */
+            symtype = OBJECT_T;
+        }
+        else {
+            /* Give up. */
+            return;
+        }
+    }
+    
+    if (!(   (symtype == wanttype)
+          || (wanttype2 != 0 && symtype == wanttype2)))
+    {
+        symtype_warning(context, sym->name, typename(symtype), typename(wanttype));
+    }
+}
+
+/* Similar, but we allow any type that has a metaclass: Object, Class, String, or Routine.
+   Generate a warning if no match. */
+extern void check_warn_symbol_has_metaclass(const assembly_operand *AO, char *context)
+{
+    symbolinfo *sym;
+    int symtype;
+    
+    if (AO->symindex < 0)
+    {
+        /* This argument is not a symbol; it's a local variable, a literal, or a computed expression. */
+        /* We can recognize and type-check some literals. */
+        if (AO->marker == DWORD_MV) {
+            symtype_warning(context, NULL, typename(DICT_WORD_REQ_T), "Object/Class/Routine/String");
+        }
+        if (AO->marker == STRING_MV) {
+            /* Strings are good here. */
+        }
+        return;
+    }
+    
+    sym = &symbols[AO->symindex];
+    symtype = sym->type;
+    
+    if (symtype == GLOBAL_VARIABLE_T)
+    {
+        /* A global variable could have any value. No way to generate a warning. */
+        return;
+    }
+    if (symtype == CONSTANT_T)
+    {
+        /* A constant could also have any value. This case also includes forward-declared constants (UNKNOWN_SFLAG). */
+        /* We try inferring its type by looking at the backpatch marker. Sadly, this only works for objects. (And not in Z-code, where object values are not backpatched.) */
+        if (sym->marker == OBJECT_MV) {
+            /* Continue with inferred type. */
+            symtype = OBJECT_T;
+        }
+        else {
+            /* Give up. */
+            return;
+        }
+    }
+
+    if (!(symtype == ROUTINE_T || symtype == CLASS_T || symtype == OBJECT_T))
+    {
+        symtype_warning(context, sym->name, typename(symtype), "Object/Class/Routine/String");
+    }
+}
+
 extern void issue_unused_warnings(void)
 {   int32 i;
 
@@ -393,14 +530,25 @@ extern void issue_unused_warnings(void)
     /*  Now back to mark anything necessary as used  */
 
     i = symbol_index("Main", -1);
-    if (!(sflags[i] & UNKNOWN_SFLAG)) sflags[i] |= USED_SFLAG;
+    if (!(symbols[i].flags & UNKNOWN_SFLAG)) symbols[i].flags |= USED_SFLAG;
 
     for (i=0;i<no_symbols;i++)
-    {   if (((sflags[i]
+    {   if (((symbols[i].flags
              & (SYSTEM_SFLAG + UNKNOWN_SFLAG + EXPORT_SFLAG
                 + INSF_SFLAG + USED_SFLAG + REPLACE_SFLAG)) == 0)
-             && (stypes[i] != OBJECT_T))
-            dbnu_warning(typename(stypes[i]), (char *) symbs[i], slines[i]);
+             && (symbols[i].type != OBJECT_T))
+            dbnu_warning(typename(symbols[i].type), symbols[i].name, symbols[i].line);
+    }
+}
+
+extern void issue_debug_symbol_warnings(void)
+{
+    int value = get_symbol_index("DEBUG");
+    if (value >= 0 && (symbols[value].flags & USED_SFLAG) && !(symbols[value].flags & UNKNOWN_SFLAG)) {
+        value = get_symbol_index("debug_flag");
+        if (value >= 0 && (symbols[value].flags & USED_SFLAG) && (symbols[value].flags & UNKNOWN_SFLAG)) {
+            warning("DEBUG mode is on, but this story or library does not appear to support it");
+        }
     }
 }
 
@@ -431,112 +579,112 @@ extern void write_the_identifier_names(void)
     for (i=0; i<NUM_ATTR_BYTES*8; i++) attribute_name_strings[i] = null_value;
 
     for (i=0; i<no_symbols; i++)
-    {   t=stypes[i];
+    {   t=symbols[i].type;
         if ((t == INDIVIDUAL_PROPERTY_T) || (t == PROPERTY_T))
-        {   if (sflags[i] & ALIASED_SFLAG)
-            {   if (individual_name_strings[svals[i]] == 0)
-                {   sprintf(idname_string, "%s", (char *) symbs[i]);
+        {   if (symbols[i].flags & ALIASED_SFLAG)
+            {   if (individual_name_strings[symbols[i].value] == 0)
+                {   sprintf(idname_string, "%s", symbols[i].name);
 
                     for (j=i+1, k=0; (j<no_symbols && k<3); j++)
-                    {   if ((stypes[j] == stypes[i])
-                            && (svals[j] == svals[i]))
+                    {   if ((symbols[j].type == symbols[i].type)
+                            && (symbols[j].value == symbols[i].value))
                         {   sprintf(idname_string+strlen(idname_string),
-                                "/%s", (char *) symbs[j]);
+                                "/%s", symbols[j].name);
                             k++;
                         }
                     }
 
-                    individual_name_strings[svals[i]]
+                    individual_name_strings[symbols[i].value]
                         = compile_string(idname_string, STRCTX_SYMBOL);
                 }
             }
             else
-            {   sprintf(idname_string, "%s", (char *) symbs[i]);
+            {   sprintf(idname_string, "%s", symbols[i].name);
 
-                individual_name_strings[svals[i]]
+                individual_name_strings[symbols[i].value]
                     = compile_string(idname_string, STRCTX_SYMBOL);
             }
         }
         if (t == ATTRIBUTE_T)
-        {   if (sflags[i] & ALIASED_SFLAG)
-            {   if (attribute_name_strings[svals[i]] == null_value)
-                {   sprintf(idname_string, "%s", (char *) symbs[i]);
+        {   if (symbols[i].flags & ALIASED_SFLAG)
+            {   if (attribute_name_strings[symbols[i].value] == null_value)
+                {   sprintf(idname_string, "%s", symbols[i].name);
 
                     for (j=i+1, k=0; (j<no_symbols && k<3); j++)
-                    {   if ((stypes[j] == stypes[i])
-                            && (svals[j] == svals[i]))
+                    {   if ((symbols[j].type == symbols[i].type)
+                            && (symbols[j].value == symbols[i].value))
                         {   sprintf(idname_string+strlen(idname_string),
-                                "/%s", (char *) symbs[j]);
+                                "/%s", symbols[j].name);
                             k++;
                         }
                     }
 
-                    attribute_name_strings[svals[i]]
+                    attribute_name_strings[symbols[i].value]
                         = compile_string(idname_string, STRCTX_SYMBOL);
                 }
             }
             else
-            {   sprintf(idname_string, "%s", (char *) symbs[i]);
+            {   sprintf(idname_string, "%s", symbols[i].name);
 
-                attribute_name_strings[svals[i]]
+                attribute_name_strings[symbols[i].value]
                     = compile_string(idname_string, STRCTX_SYMBOL);
             }
         }
-        if (sflags[i] & ACTION_SFLAG)
-        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+        if (symbols[i].flags & ACTION_SFLAG)
+        {   sprintf(idname_string, "%s", symbols[i].name);
             idname_string[strlen(idname_string)-3] = 0;
 
             if (debugfile_switch)
             {   debug_file_printf("<action>");
                 debug_file_printf
                     ("<identifier>##%s</identifier>", idname_string);
-                debug_file_printf("<value>%d</value>", svals[i]);
+                debug_file_printf("<value>%d</value>", symbols[i].value);
                 debug_file_printf("</action>");
             }
 
-            action_name_strings[svals[i]]
+            action_name_strings[symbols[i].value]
                 = compile_string(idname_string, STRCTX_SYMBOL);
         }
     }
 
     for (i=0; i<no_symbols; i++)
-    {   if (stypes[i] == FAKE_ACTION_T)
-        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+    {   if (symbols[i].type == FAKE_ACTION_T)
+        {   sprintf(idname_string, "%s", symbols[i].name);
             idname_string[strlen(idname_string)-3] = 0;
 
-            action_name_strings[svals[i]
+            action_name_strings[symbols[i].value
                     - ((grammar_version_number==1)?256:4096) + no_actions]
                 = compile_string(idname_string, STRCTX_SYMBOL);
         }
     }
 
     for (j=0; j<no_arrays; j++)
-    {   i = array_symbols[j];
-        sprintf(idname_string, "%s", (char *) symbs[i]);
+    {   i = arrays[j].symbol;
+        sprintf(idname_string, "%s", symbols[i].name);
 
         array_name_strings[j]
             = compile_string(idname_string, STRCTX_SYMBOL);
     }
   if (define_INFIX_switch)
   { for (i=0; i<no_symbols; i++)
-    {   if (stypes[i] == GLOBAL_VARIABLE_T)
-        {   sprintf(idname_string, "%s", (char *) symbs[i]);
-            array_name_strings[no_arrays + svals[i] -16]
+    {   if (symbols[i].type == GLOBAL_VARIABLE_T)
+        {   sprintf(idname_string, "%s", symbols[i].name);
+            array_name_strings[no_arrays + symbols[i].value -16]
                 = compile_string(idname_string, STRCTX_SYMBOL);
         }
     }
 
     for (i=0; i<no_named_routines; i++)
-    {   sprintf(idname_string, "%s", (char *) symbs[named_routine_symbols[i]]);
+    {   sprintf(idname_string, "%s", symbols[named_routine_symbols[i]].name);
             array_name_strings[no_arrays + no_globals + i]
                 = compile_string(idname_string, STRCTX_SYMBOL);
     }
 
     for (i=0, no_named_constants=0; i<no_symbols; i++)
-    {   if (((stypes[i] == OBJECT_T) || (stypes[i] == CLASS_T)
-            || (stypes[i] == CONSTANT_T))
-            && ((sflags[i] & (UNKNOWN_SFLAG+ACTION_SFLAG))==0))
-        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+    {   if (((symbols[i].type == OBJECT_T) || (symbols[i].type == CLASS_T)
+            || (symbols[i].type == CONSTANT_T))
+            && ((symbols[i].flags & (UNKNOWN_SFLAG+ACTION_SFLAG))==0))
+        {   sprintf(idname_string, "%s", symbols[i].name);
             array_name_strings[no_arrays + no_globals + no_named_routines
                 + no_named_constants++]
                 = compile_string(idname_string, STRCTX_SYMBOL);
@@ -551,36 +699,29 @@ extern void write_the_identifier_names(void)
 /* ------------------------------------------------------------------------- */
 
 static void assign_symbol_base(int index, int32 value, int type)
-{   svals[index]  = value;
-    stypes[index] = type;
-    if (sflags[index] & UNKNOWN_SFLAG)
-    {   sflags[index] &= (~UNKNOWN_SFLAG);
-        if (is_systemfile()) sflags[index] |= INSF_SFLAG;
-        slines[index] = get_brief_location(&ErrorReport);
+{   symbols[index].value  = value;
+    symbols[index].type = type;
+    if (symbols[index].flags & UNKNOWN_SFLAG)
+    {   symbols[index].flags &= (~UNKNOWN_SFLAG);
+        if (is_systemfile()) symbols[index].flags |= INSF_SFLAG;
+        symbols[index].line = get_brief_location(&ErrorReport);
     }
 }
 
 extern void assign_symbol(int index, int32 value, int type)
 {
-    if (!glulx_mode) {
-        assign_symbol_base(index, value, type);
-    }
-    else {
-        smarks[index] = 0;
-        assign_symbol_base(index, value, type);
-    }
+    assign_symbol_base(index, value, type);
+    symbols[index].marker = 0;
+    if (symdef_trace_setting)
+        printf("Defined symbol %d '%s' as %d (%s)\n", index, symbols[index].name, value, typename(type));
 }
 
 extern void assign_marked_symbol(int index, int marker, int32 value, int type)
 {
-    if (!glulx_mode) {
-        assign_symbol_base(index, (int32)marker*0x10000 + (value % 0x10000),
-            type);
-    }
-    else {
-        smarks[index] = marker;
-        assign_symbol_base(index, value, type);
-    }
+    assign_symbol_base(index, value, type);
+    symbols[index].marker = marker;
+    if (symdef_trace_setting)
+        printf("Defined symbol %d '%s' as %s %d (%s)\n", index, symbols[index].name, describe_mv(marker), value, typename(type));
 }
 
 static void emit_debug_information_for_predefined_symbol
@@ -633,21 +774,21 @@ static void emit_debug_information_for_predefined_symbol
 
 static void create_symbol(char *p, int32 value, int type)
 {   int i = symbol_index(p, -1);
-    if (!(sflags[i] & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG))) {
+    if (!(symbols[i].flags & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG))) {
         /* Symbol already defined! */
-        if (svals[i] == value && stypes[i] == type) {
+        if (symbols[i].value == value && symbols[i].type == type) {
             /* Special case: the symbol was already defined with this same
                value. We let it pass. */
             return;
         }
         else {
-            ebf_symbol_error("new symbol", p, typename(stypes[i]), slines[i]);
+            ebf_symbol_error("new symbol", p, typename(symbols[i].type), symbols[i].line);
             return;
         }
     }
-    svals[i] = value; stypes[i] = type; slines[i] = blank_brief_location;
+    symbols[i].value = value; symbols[i].type = type; symbols[i].line = blank_brief_location;
     /* If the symbol already existed with REDEFINABLE_SFLAG, we keep that. */
-    sflags[i] = USED_SFLAG + SYSTEM_SFLAG + (sflags[i] & REDEFINABLE_SFLAG);
+    symbols[i].flags = USED_SFLAG + SYSTEM_SFLAG + (symbols[i].flags & REDEFINABLE_SFLAG);
     emit_debug_information_for_predefined_symbol(p, i, value, type);
 }
 
@@ -655,8 +796,8 @@ static void create_rsymbol(char *p, int value, int type)
 {   int i = symbol_index(p, -1);
     /* This is only called for a few symbols with known names.
        They will not collide. */
-    svals[i] = value; stypes[i] = type; slines[i] = blank_brief_location;
-    sflags[i] = USED_SFLAG + SYSTEM_SFLAG + REDEFINABLE_SFLAG;
+    symbols[i].value = value; symbols[i].type = type; symbols[i].line = blank_brief_location;
+    symbols[i].flags = USED_SFLAG + SYSTEM_SFLAG + REDEFINABLE_SFLAG;
     emit_debug_information_for_predefined_symbol(p, i, value, type);
 }
 
@@ -698,7 +839,8 @@ static void stockup_symbols(void)
     }
 
     create_symbol("WORDSIZE",        WORDSIZE, CONSTANT_T);
-    create_symbol("DICT_ENTRY_BYTES", DICT_ENTRY_BYTE_LENGTH, CONSTANT_T);
+    /* DICT_ENTRY_BYTES must be REDEFINABLE_SFLAG because the Version directive can change it. */
+    create_rsymbol("DICT_ENTRY_BYTES", DICT_ENTRY_BYTE_LENGTH, CONSTANT_T);
     if (!glulx_mode) {
         create_symbol("DICT_WORD_SIZE", ((version_number==3)?4:6), CONSTANT_T);
         create_symbol("NUM_ATTR_BYTES", ((version_number==3)?4:6), CONSTANT_T);
@@ -820,7 +962,7 @@ extern void add_symbol_replacement_mapping(int original, int renamed)
     int ix;
 
     if (original == renamed) {
-        error_named("A routine cannot be 'Replace'd to itself:", (char *)symbs[original]);
+        error_named("A routine cannot be 'Replace'd to itself:", symbols[original].name);
         return;        
     }
 
@@ -841,10 +983,10 @@ extern void add_symbol_replacement_mapping(int original, int renamed)
 
     for (ix=0; ix<symbol_replacements_count; ix++) {
         if (original == symbol_replacements[ix].original_symbol) {
-            error_named("A routine cannot be 'Replace'd to more than one new name:", (char *)symbs[original]);
+            error_named("A routine cannot be 'Replace'd to more than one new name:", symbols[original].name);
         }
         if (renamed == symbol_replacements[ix].original_symbol) {
-            error_named("A routine cannot be 'Replace'd to a 'Replace'd name:", (char *)symbs[original]);
+            error_named("A routine cannot be 'Replace'd to a 'Replace'd name:", symbols[original].name);
         }
     }
 
@@ -1039,13 +1181,16 @@ extern void df_note_function_symbol(int symbol)
        of a function being defined.) */
     if (df_dont_note_global_symbols)
         return;
+    /* If we're compiling an unreachable statement, no reference. */
+    if (execution_never_reaches_here)
+        return;
 
     /* We are only interested in functions, or forward-declared symbols
        that might turn out to be functions. */
-    symtype = stypes[symbol];
+    symtype = symbols[symbol].type;
     if (symtype != ROUTINE_T && symtype != CONSTANT_T)
         return;
-    if (symtype == CONSTANT_T && !(sflags[symbol] & UNKNOWN_SFLAG))
+    if (symtype == CONSTANT_T && !(symbols[symbol].flags & UNKNOWN_SFLAG))
         return;
 
     bucket = (df_current_function_addr ^ (uint32)symbol) % DF_SYMBOL_HASH_BUCKETS;
@@ -1094,15 +1239,15 @@ extern void locate_dead_functions(void)
        we'll mark them specially. */
 
     ix = symbol_index("Main__", -1);
-    if (stypes[ix] == ROUTINE_T) {
-        uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor);
+    if (symbols[ix].type == ROUTINE_T) {
+        uint32 addr = symbols[ix].value * (glulx_mode ? 1 : scale_factor);
         tofunc = df_function_for_address(addr);
         if (tofunc)
             tofunc->usage |= DF_USAGE_MAIN;
     }
     ix = symbol_index("Main", -1);
-    if (stypes[ix] == ROUTINE_T) {
-        uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor);
+    if (symbols[ix].type == ROUTINE_T) {
+        uint32 addr = symbols[ix].value * (glulx_mode ? 1 : scale_factor);
         tofunc = df_function_for_address(addr);
         if (tofunc)
             tofunc->usage |= DF_USAGE_MAIN;
@@ -1120,12 +1265,12 @@ extern void locate_dead_functions(void)
     for (ent = func->refs; ent; ent=ent->refsnext) {
         uint32 addr;
         int symbol = ent->symbol;
-        if (stypes[symbol] != ROUTINE_T)
+        if (symbols[symbol].type != ROUTINE_T)
             continue;
-        addr = svals[symbol] * (glulx_mode ? 1 : scale_factor);
+        addr = symbols[symbol].value * (glulx_mode ? 1 : scale_factor);
         tofunc = df_function_for_address(addr);
         if (!tofunc) {
-            error_named("Internal error in stripping: global ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]);
+            error_named("Internal error in stripping: global ROUTINE_T symbol is not found in df_function map:", symbols[symbol].name);
             continue;
         }
         /* A function may be marked here more than once. That's fine. */
@@ -1177,12 +1322,12 @@ extern void locate_dead_functions(void)
             for (ent = func->refs; ent; ent=ent->refsnext) {
                 uint32 addr;
                 int symbol = ent->symbol;
-                if (stypes[symbol] != ROUTINE_T)
+                if (symbols[symbol].type != ROUTINE_T)
                     continue;
-                addr = svals[symbol] * (glulx_mode ? 1 : scale_factor);
+                addr = symbols[symbol].value * (glulx_mode ? 1 : scale_factor);
                 tofunc = df_function_for_address(addr);
                 if (!tofunc) {
-                    error_named("Internal error in stripping: function ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]);
+                    error_named("Internal error in stripping: function ROUTINE_T symbol is not found in df_function map:", symbols[symbol].name);
                     continue;
                 }
                 if (tofunc->usage)
@@ -1391,18 +1536,14 @@ extern uint32 df_next_function_iterate(int *funcused)
 
 extern void init_symbols_vars(void)
 {
-    symbs = NULL;
-    svals = NULL;
-    smarks = NULL;
-    stypes = NULL;
-    sflags = NULL;
-    next_entry = NULL;
+    symbols = NULL;
     start_of_list = NULL;
+    symbol_debug_info = NULL;
 
     symbol_name_space_chunks = NULL;
     no_symbol_name_space_chunks = 0;
     symbols_free_space=NULL;
-    symbols_ceiling=symbols_free_space;
+    symbols_ceiling=NULL;
 
     no_symbols = 0;
 
@@ -1433,28 +1574,21 @@ extern void symbols_begin_pass(void)
 
 extern void symbols_allocate_arrays(void)
 {
-    symbs      = my_calloc(sizeof(char *),  MAX_SYMBOLS, "symbols");
-    svals      = my_calloc(sizeof(int32),   MAX_SYMBOLS, "symbol values");
-    if (glulx_mode)
-        smarks = my_calloc(sizeof(int),     MAX_SYMBOLS, "symbol markers");
-    slines     = my_calloc(sizeof(brief_location), MAX_SYMBOLS, "symbol lines");
-    stypes     = my_calloc(sizeof(char),    MAX_SYMBOLS, "symbol types");
-    sflags     = my_calloc(sizeof(int),     MAX_SYMBOLS, "symbol flags");
+    initialise_memory_list(&symbols_memlist,
+        sizeof(symbolinfo), 6400, (void**)&symbols,
+        "symbols");
     if (debugfile_switch)
-    {   symbol_debug_backpatch_positions =
-            my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS,
-                      "symbol debug information backpatch positions");
-        replacement_debug_backpatch_positions =
-            my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS,
-                      "replacement debug information backpatch positions");
+    {
+        initialise_memory_list(&symbol_debug_info_memlist,
+            sizeof(symboldebuginfo), 6400, (void**)&symbol_debug_info,
+            "symbol debug backpatch info");
     }
-    next_entry = my_calloc(sizeof(int),     MAX_SYMBOLS,
-                     "symbol linked-list forward links");
     start_of_list = my_calloc(sizeof(int32), HASH_TAB_SIZE,
                      "hash code list beginnings");
 
-    symbol_name_space_chunks
-        = my_calloc(sizeof(char *), MAX_SYMBOL_CHUNKS, "symbol names chunk addresses");
+    initialise_memory_list(&symbol_name_space_chunks_memlist,
+        sizeof(char *), 32, (void**)&symbol_name_space_chunks,
+        "symbol names chunk addresses");
 
     if (track_unused_routines) {
         df_tables_closed = FALSE;
@@ -1495,23 +1629,13 @@ extern void symbols_free_arrays(void)
         my_free(&(symbol_name_space_chunks[i]),
             "symbol names chunk");
 
-    my_free(&symbol_name_space_chunks, "symbol names chunk addresses");
+    deallocate_memory_list(&symbol_name_space_chunks_memlist);
 
-    my_free(&symbs, "symbols");
-    my_free(&svals, "symbol values");
-    my_free(&smarks, "symbol markers");
-    my_free(&slines, "symbol lines");
-    my_free(&stypes, "symbol types");
-    my_free(&sflags, "symbol flags");
+    deallocate_memory_list(&symbols_memlist);
     if (debugfile_switch)
-    {   my_free
-            (&symbol_debug_backpatch_positions,
-             "symbol debug information backpatch positions");
-        my_free
-            (&replacement_debug_backpatch_positions,
-             "replacement debug information backpatch positions");
+    {
+        deallocate_memory_list(&symbol_debug_info_memlist);
     }
-    my_free(&next_entry, "symbol linked-list forward links");
     my_free(&start_of_list, "hash code list beginnings");
 
     if (symbol_replacements)
index 982d49f101ff934383b19d180d42ededd6f73600..f99de050b87089721355ca52cd7e597df316f608 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "syntax" : Syntax analyser and compiler                                 */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -15,7 +15,7 @@
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -146,9 +146,9 @@ extern void parse_program(char *source)
 
 extern int parse_directive(int internal_flag)
 {
-    /*  Internal_flag is FALSE if the directive is encountered normally,
-        TRUE if encountered with a # prefix inside a routine or object
-        definition.
+    /*  Internal_flag is FALSE if the directive is encountered normally
+        (at the top level of the program); TRUE if encountered with 
+        a # prefix inside a routine or object definition.
 
         (Only directives like #ifdef are permitted inside a definition.)
 
@@ -158,6 +158,11 @@ extern int parse_directive(int internal_flag)
     int is_renamed;
 
     begin_syntax_line(FALSE);
+    if (!internal_flag) {
+        /* An internal directive can occur in the middle of an expression or
+           object definition. So we only release for top-level directives.   */
+        release_token_texts();
+    }
     get_next_token();
 
     if (token_type == EOF_TT) return(FALSE);
@@ -184,9 +189,9 @@ extern int parse_directive(int internal_flag)
         {   ebf_error("routine name", token_text);
             return(FALSE);
         }
-        if ((!(sflags[token_value] & UNKNOWN_SFLAG))
-            && (!(sflags[token_value] & REPLACE_SFLAG)))
-        {   ebf_symbol_error("routine name", token_text, typename(stypes[token_value]), slines[token_value]);
+        if ((!(symbols[token_value].flags & UNKNOWN_SFLAG))
+            && (!(symbols[token_value].flags & REPLACE_SFLAG)))
+        {   ebf_symbol_error("routine name", token_text, typename(symbols[token_value].type), symbols[token_value].line);
             return(FALSE);
         }
 
@@ -195,7 +200,7 @@ extern int parse_directive(int internal_flag)
         rep_symbol = routine_symbol;
         is_renamed = find_symbol_replacement(&rep_symbol);
 
-        if ((sflags[routine_symbol] & REPLACE_SFLAG) 
+        if ((symbols[routine_symbol].flags & REPLACE_SFLAG) 
             && !is_renamed && (is_systemfile()))
         {   /* The function is definitely being replaced (system_file
                always loses priority in a replacement) but is not
@@ -214,9 +219,9 @@ extern int parse_directive(int internal_flag)
         {   /* Parse the function definition and assign its symbol. */
             assign_symbol(routine_symbol,
                 parse_routine(lexical_source, FALSE,
-                    (char *) symbs[routine_symbol], FALSE, routine_symbol),
+                    symbols[routine_symbol].name, FALSE, routine_symbol),
                 ROUTINE_T);
-            slines[routine_symbol] = routine_starts_line;
+            symbols[routine_symbol].line = routine_starts_line;
         }
 
         if (is_renamed) {
@@ -224,8 +229,8 @@ extern int parse_directive(int internal_flag)
                The first time we see a definition for symbol X, we
                copy it to Y -- that's the "original" form of the
                function. */
-            if (svals[rep_symbol] == 0) {
-                assign_symbol(rep_symbol, svals[routine_symbol], ROUTINE_T);
+            if (symbols[rep_symbol].value == 0) {
+                assign_symbol(rep_symbol, symbols[routine_symbol].value, ROUTINE_T);
             }
         }
 
@@ -237,13 +242,13 @@ extern int parse_directive(int internal_flag)
         return TRUE;
     }
 
-    if ((token_type == SYMBOL_TT) && (stypes[token_value] == CLASS_T))
+    if ((token_type == SYMBOL_TT) && (symbols[token_value].type == CLASS_T))
     {   if (internal_flag)
         {   error("It is illegal to nest an object in a routine using '#classname'");
             return(TRUE);
         }
-        sflags[token_value] |= USED_SFLAG;
-        make_object(FALSE, NULL, -1, -1, svals[token_value]);
+        symbols[token_value].flags |= USED_SFLAG;
+        make_object(FALSE, NULL, -1, -1, symbols[token_value].value);
         return TRUE;
     }
 
@@ -261,6 +266,7 @@ extern int parse_directive(int internal_flag)
     return !(parse_given_directive(internal_flag));
 }
 
+/* Check what's coming up after a switch case value. */
 static int switch_sign(void)
 {
     if ((token_type == SEP_TT)&&(token_value == COLON_SEP))   return 1;
@@ -269,8 +275,10 @@ static int switch_sign(void)
     return 0;
 }
 
-static assembly_operand spec_stack[32];
-static int spec_type[32];
+/* Info for the current switch statement. Both arrays indexed by spec_sp */
+#define MAX_SPEC_STACK (32)
+static assembly_operand spec_stack[MAX_SPEC_STACK];
+static int spec_type[MAX_SPEC_STACK];
 
 static void compile_alternatives_z(assembly_operand switch_value, int n,
     int stack_level, int label, int flag)
@@ -324,7 +332,7 @@ static void parse_switch_spec(assembly_operand switch_value, int label,
     sequence_point_follows = FALSE;
 
     do
-    {   if (spec_sp == 32)
+    {   if (spec_sp >= MAX_SPEC_STACK)
         {   error("At most 32 values can be given in a single 'switch' case");
             panic_mode_error_recovery();
             return;
@@ -444,7 +452,8 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name,
 
     no_locals = 0;
 
-    for (i=0;i<MAX_LOCAL_VARIABLES-1;i++) local_variables.keywords[i] = "";
+    for (i=0;i<MAX_LOCAL_VARIABLES-1;i++)
+        local_variable_names[i].text[0] = 0;
 
     do
     {   statements.enabled = TRUE;
@@ -477,12 +486,15 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name,
             break;
         }
 
-        for (i=0;i<no_locals;i++)
-            if (strcmpcis(token_text, local_variables.keywords[i])==0)
+        for (i=0;i<no_locals;i++) {
+            if (strcmpcis(token_text, local_variable_names[i].text)==0)
                 error_named("Local variable defined twice:", token_text);
-        local_variables.keywords[no_locals++] = token_text;
+        }
+        strcpy(local_variable_names[no_locals++].text, token_text);
     } while(TRUE);
 
+    /* Set up the local variable hash and the local_variables.keywords
+       table. */
     construct_local_variable_tables();
 
     if ((trace_fns_setting==3)
@@ -490,14 +502,14 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name,
         || ((trace_fns_setting==1) && (is_systemfile()==FALSE)))
         debug_flag = TRUE;
     if ((embedded_flag == FALSE) && (veneer_mode == FALSE) && debug_flag)
-        sflags[r_symbol] |= STAR_SFLAG;
+        symbols[r_symbol].flags |= STAR_SFLAG;
 
     packed_address = assemble_routine_header(no_locals, debug_flag,
         name, embedded_flag, r_symbol);
 
     do
     {   begin_syntax_line(TRUE);
-
+        release_token_texts();
         get_next_token();
 
         if (token_type == EOF_TT)
@@ -600,17 +612,36 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name,
     return packed_address;
 }
 
+/* Parse one block of code (a statement or brace-delimited stanza).
+   This is used by the IF, DO, FOR, OBJECTLOOP, SWITCH, and WHILE
+   statements.
+   (Note that this is *not* called by the top-level parse_routine() 
+   handler.)
+   The break_label and continue_label arguments are the labels in
+   the calling block to jump to on "break" or "continue". -1 means
+   we can't "break"/"continue" here (because we're not in a loop/switch).
+   If switch_rule is true, we're in a switch block; case labels are
+   accepted.
+*/
 extern void parse_code_block(int break_label, int continue_label,
     int switch_rule)
-{   int switch_clause_made = FALSE, default_clause_made = FALSE, switch_label = 0,
-        unary_minus_flag;
+{   int switch_clause_made = FALSE, default_clause_made = FALSE, switch_label = 0;
+    int unary_minus_flag, saved_entire_flag;
+
+    saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
+    if (execution_never_reaches_here)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
 
     begin_syntax_line(TRUE);
+    release_token_texts();
     get_next_token();
 
     if (token_type == SEP_TT && token_value == OPEN_BRACE_SEP)
-    {   do
+    {
+        /* Parse a braced stanza of statements. */
+        do
         {   begin_syntax_line(TRUE);
+            release_token_texts();
             get_next_token();
             
             if ((token_type == SEP_TT) && (token_value == HASH_SEP))
@@ -620,10 +651,12 @@ extern void parse_code_block(int break_label, int continue_label,
             if (token_type == SEP_TT && token_value == CLOSE_BRACE_SEP)
             {   if (switch_clause_made && (!default_clause_made))
                     assemble_label_no(switch_label);
-                return;
+                break;
             }
             if (token_type == EOF_TT)
-            {   ebf_error("'}'", token_text); return; }
+            {   ebf_error("'}'", token_text);
+                break;
+            }
 
             if (switch_rule != 0)
             {
@@ -706,12 +739,18 @@ extern void parse_code_block(int break_label, int continue_label,
         }
         while(TRUE);
     }
+    else {
+        if (switch_rule != 0)
+            ebf_error("braced code block after 'switch'", token_text);
+        
+        /* Parse a single statement. */
+        parse_statement(break_label, continue_label);
+    }
 
-    if (switch_rule != 0)
-        ebf_error("braced code block after 'switch'", token_text);
-
-    parse_statement(break_label, continue_label);
-    return;
+    if (saved_entire_flag)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    else
+        execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
 }
 
 /* ========================================================================= */
index 4158253d8e5af79a89fc1bf38db8e6d8d301a627..389eb4bca1506a9d4aa99fe161d461717442ea30 100644 (file)
@@ -3,8 +3,8 @@
 /*               end of dynamic memory, gluing together all the required     */
 /*               tables.                                                     */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -17,7 +17,7 @@
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -122,8 +122,20 @@ extern void write_serial_number(char *buffer)
 #endif
 }
 
-static void percentage(char *name, int32 x, int32 total)
-{   printf("   %-20s %2d.%d%%\n",name,x*100/total,(x*1000/total)%10);
+static char percentage_buffer[32];
+
+static char *show_percentage(int32 x, int32 total)
+{
+    if (memory_map_setting < 2) {
+        percentage_buffer[0] = '\0';
+    }
+    else if (x == 0) {
+        sprintf(percentage_buffer, "  ( --- )");
+    }
+    else {
+        sprintf(percentage_buffer, "  (%.1f %%)", (float)x * 100.0 / (float)total);
+    }
+    return percentage_buffer;
 }
 
 static char *version_name(int v)
@@ -154,7 +166,7 @@ static int32 rough_size_of_paged_memory_z(void)
     ASSERT_ZCODE();
 
     total = 64                                                     /* header */
-            + 2 + subtract_pointers(low_strings_top, low_strings)
+            + 2 + low_strings_top
                                                          /* low strings pool */
             + 6*32;                                   /* abbreviations table */
 
@@ -188,7 +200,7 @@ static int32 rough_size_of_paged_memory_z(void)
               + 2*no_actions                              /* action routines */
               + 2*no_grammar_token_routines;     /* general parsing routines */
 
-    total += (subtract_pointers(dictionary_top, dictionary))  /* dictionary */
+    total += (dictionary_top)                            /* dictionary size */
              + ((module_switch)?30:0);                        /* module map */
 
     total += static_array_area_size;                       /* static arrays */
@@ -212,7 +224,8 @@ static int32 rough_size_of_paged_memory_g(void)
     /* No header for us! */
     total = 1000; /* bit of a fudge factor */
 
-    total += dynamic_array_area_size; /* arrays and global variables */
+    total += no_globals * 4; /* global variables */
+    total += dynamic_array_area_size; /* arrays */
 
     total += no_objects * OBJECT_BYTE_LENGTH; /* object tables */
     total += properties_table_size; /* property tables */
@@ -233,7 +246,7 @@ static int32 rough_size_of_paged_memory_g(void)
     total += 4 + no_actions * 4; /* actions functions table */
 
     total += 4;
-    total += subtract_pointers(dictionary_top, dictionary);
+    total += dictionary_top;
 
     while (total % GPAGESIZE)
       total++;
@@ -291,7 +304,7 @@ static void construct_storyfile_z(void)
     p[mark]=0x80; p[mark+1]=0; mark+=2;        /* Start the low strings pool
                                          with a useful default string, "   " */
 
-    for (i=0; i+low_strings<low_strings_top; mark++, i++) /* Low strings pool */
+    for (i=0; i<low_strings_top; mark++, i++)  /* Low strings pool */
         p[0x42+i]=low_strings[i];
 
     abbrevs_at = mark;
@@ -309,7 +322,7 @@ static void construct_storyfile_z(void)
        strings". Write the abbreviations after these. */
     k = abbrevs_at+2*MAX_DYNAMIC_STRINGS;
     for (i=0; i<no_abbreviations; i++)
-    {   j=abbrev_values[i];
+    {   j=abbreviations[i].value;
         p[k++]=j/256;
         p[k++]=j%256;
     }
@@ -361,8 +374,7 @@ static void construct_storyfile_z(void)
     /*  -------------------- Objects and Properties ------------------------ */
 
     /* The object table must be word-aligned. The Z-machine spec does not
-       require this, but the RA__Pr() veneer routine does. See 
-       http://inform7.com/mantis/view.php?id=1712.
+       require this, but the RA__Pr() veneer routine does.
     */
     while ((mark%2) != 0) p[mark++]=0;
 
@@ -371,8 +383,8 @@ static void construct_storyfile_z(void)
     p[mark++]=0; p[mark++]=0;
 
     for (i=2; i< ((version_number==3)?32:64); i++)
-    {   p[mark++]=prop_default_value[i]/256;
-        p[mark++]=prop_default_value[i]%256;
+    {   p[mark++]=commonprops[i].default_value/256;
+        p[mark++]=commonprops[i].default_value%256;
     }
 
     object_tree_at = mark;
@@ -428,11 +440,11 @@ static void construct_storyfile_z(void)
 
     class_numbers_offset = mark;
     for (i=0; i<no_classes; i++)
-    {   p[mark++] = class_object_numbers[i]/256;
-        p[mark++] = class_object_numbers[i]%256;
+    {   p[mark++] = class_info[i].object_number/256;
+        p[mark++] = class_info[i].object_number%256;
         if (module_switch)
-        {   p[mark++] = class_begins_at[i]/256;
-            p[mark++] = class_begins_at[i]%256;
+        {   p[mark++] = class_info[i].begins_at/256;
+            p[mark++] = class_info[i].begins_at%256;
         }
     }
     p[mark++] = 0;
@@ -480,7 +492,7 @@ static void construct_storyfile_z(void)
 
     if (define_INFIX_switch)
     {   for (i=0, k=1, l=0; i<no_named_routines; i++)
-        {   if (sflags[named_routine_symbols[i]] & STAR_SFLAG) l=l+k;
+        {   if (symbols[named_routine_symbols[i]].flags & STAR_SFLAG) l=l+k;
             k=k*2;
             if (k==256) { p[mark++] = l; k=1; l=0; }
         }
@@ -581,7 +593,7 @@ table format requested (producing number 2 format instead)");
 
         for (i=0; i<no_adjectives; i++)
         {   j = final_dict_order[adjectives[no_adjectives-i-1]]
-                *((version_number==3)?7:9)
+                *DICT_ENTRY_BYTE_LENGTH
                 + dictionary_offset + 7;
             p[mark++]=j/256; p[mark++]=j%256; p[mark++]=0;
             p[mark++]=(256-no_adjectives+i);
@@ -601,19 +613,19 @@ table format requested (producing number 2 format instead)");
                      dictionary[2]=',';                 /* force words apart */
                      dictionary[3]='"';
 
-    dictionary[4]=(version_number==3)?7:9;           /* Length of each entry */
+    dictionary[4]=DICT_ENTRY_BYTE_LENGTH;           /* Length of each entry */
     dictionary[5]=(dict_entries/256);                   /* Number of entries */
     dictionary[6]=(dict_entries%256);
 
     for (i=0; i<7; i++) p[mark++] = dictionary[i];
 
     for (i=0; i<dict_entries; i++)
-    {   k = 7 + i*((version_number==3)?7:9);
-        j = mark + final_dict_order[i]*((version_number==3)?7:9);
-        for (l = 0; l<((version_number==3)?7:9); l++)
+    {   k = 7 + i*DICT_ENTRY_BYTE_LENGTH;
+        j = mark + final_dict_order[i]*DICT_ENTRY_BYTE_LENGTH;
+        for (l = 0; l<DICT_ENTRY_BYTE_LENGTH; l++)
             p[j++] = dictionary[k++];
     }
-    mark += dict_entries * ((version_number==3)?7:9);
+    mark += dict_entries * DICT_ENTRY_BYTE_LENGTH;
 
     /*  ------------------------- Module Map ------------------------------- */
 
@@ -643,7 +655,7 @@ table format requested (producing number 2 format instead)");
 size of the Z-machine format. See the memory map below: the start \
 of the area marked \"above readable memory\" must be brought down to $FFFE \
 or less.");
-        memory_map_switch = TRUE;
+        memory_map_setting = 1;
         /* Backpatching the grammar tables requires us to trust some of the */
         /* addresses we've written into Z-machine memory, but they may have */
         /* been truncated to 16 bits, so we can't do it.                    */
@@ -912,7 +924,7 @@ or less.");
 
         mark = actions_at;
         for (i=0; i<no_actions; i++)
-        {   j=action_byte_offset[i];
+        {   j=actions[i].byte_offset;
             if (OMIT_UNUSED_ROUTINES)
                 j = df_stripped_address_for_address(j);
             j += code_offset/scale_factor;
@@ -942,7 +954,7 @@ or less.");
                         switch(topbits)
                         {   case 1:
                                 value = final_dict_order[value]
-                                        *((version_number==3)?7:9)
+                                        *DICT_ENTRY_BYTE_LENGTH
                                         + dictionary_offset + 7;
                                 break;
                             case 2:
@@ -962,115 +974,6 @@ or less.");
 
     /*  ---- From here on, it's all reportage: construction is finished ---- */
 
-    if (statistics_switch)
-    {   int32 k_long, rate; char *k_str="";
-        k_long=(Out_Size/1024);
-        if ((Out_Size-1024*k_long) >= 512) { k_long++; k_str=""; }
-        else if ((Out_Size-1024*k_long) > 0) { k_str=".5"; }
-        if (total_bytes_trans == 0) rate = 0;
-        else rate=total_bytes_trans*1000/total_chars_trans;
-
-        {   printf("In:\
-%3d source code files            %6d syntactic lines\n\
-%6d textual lines              %8ld characters ",
-            total_input_files, no_syntax_lines,
-            total_source_line_count, (long int) total_chars_read);
-            if (character_set_unicode) printf("(UTF-8)\n");
-            else if (character_set_setting == 0) printf("(plain ASCII)\n");
-            else
-            {   printf("(ISO 8859-%d %s)\n", character_set_setting,
-                    name_of_iso_set(character_set_setting));
-            }
-
-            printf("Allocated:\n\
-%6d symbols (maximum %4d)    %8ld bytes of memory\n\
-Out:   Version %d \"%s\" %s %d.%c%c%c%c%c%c (%ld%sK long):\n",
-                 no_symbols, MAX_SYMBOLS,
-                 (long int) malloced_bytes,
-                 version_number,
-                 version_name(version_number),
-                 output_called,
-                 release_number, p[18], p[19], p[20], p[21], p[22], p[23],
-                 (long int) k_long, k_str);
-
-            printf("\
-%6d classes (maximum %3d)        %6d objects (maximum %3d)\n\
-%6d global vars (maximum 233)    %6d variable/array space (maximum %d)\n",
-                 no_classes, MAX_CLASSES,
-                 no_objects, ((version_number==3)?255:(MAX_OBJECTS-1)),
-                 no_globals,
-                 dynamic_array_area_size, MAX_STATIC_DATA);
-
-            printf(
-"%6d verbs (maximum %3d)          %6d dictionary entries (maximum %d)\n\
-%6d grammar lines (version %d)    %6d grammar tokens (unlimited)\n\
-%6d actions (maximum %3d)        %6d attributes (maximum %2d)\n\
-%6d common props (maximum %2d)    %6d individual props (unlimited)\n",
-                 no_Inform_verbs, MAX_VERBS,
-                 dict_entries, MAX_DICT_ENTRIES,
-                 no_grammar_lines, grammar_version_number,
-                 no_grammar_tokens,
-                 no_actions, MAX_ACTIONS,
-                 no_attributes, ((version_number==3)?32:48),
-                 no_properties-2, ((version_number==3)?30:62),
-                 no_individual_properties - 64);
-
-            if (track_unused_routines)
-            {
-                uint32 diff = df_total_size_before_stripping - df_total_size_after_stripping;
-                printf(
-"%6ld bytes of Z-code              %6ld unused bytes %s (%.1f%%)\n",
-                 (long int) df_total_size_before_stripping, (long int) diff,
-                 (OMIT_UNUSED_ROUTINES ? "stripped out" : "detected"),
-                 100 * (float)diff / (float)df_total_size_before_stripping);
-            }
-
-            printf(
-"%6ld characters used in text      %6ld bytes compressed (rate %d.%3ld)\n\
-%6d abbreviations (maximum %d)   %6d routines (unlimited)\n\
-%6ld instructions of Z-code       %6d sequence points\n\
-%6ld bytes readable memory used (maximum 65536)\n\
-%6ld bytes used in Z-machine      %6ld bytes free in Z-machine\n",
-                 (long int) total_chars_trans,
-                 (long int) total_bytes_trans,
-                 (total_chars_trans>total_bytes_trans)?0:1,
-                 (long int) rate,
-                 no_abbreviations, MAX_ABBREVS,
-                 no_routines,
-                 (long int) no_instructions, no_sequence_points,
-                 (long int) Write_Code_At,
-                 (long int) Out_Size,
-                 (long int)
-                      (((long int) (limit*1024L)) - ((long int) Out_Size)));
-
-        }
-    }
-
-    if (offsets_switch)
-    {
-        {   printf(
-"\nOffsets in %s:\n\
-%05lx Synonyms     %05lx Defaults     %05lx Objects    %05lx Properties\n\
-%05lx Variables    %05lx Parse table  %05lx Actions    %05lx Preactions\n\
-%05lx Adjectives   %05lx Dictionary   %05lx Code       %05lx Strings\n",
-            output_called,
-            (long int) abbrevs_at,
-            (long int) prop_defaults_at,
-            (long int) object_tree_at,
-            (long int) object_props_at,
-            (long int) globals_at,
-            (long int) grammar_table_at,
-            (long int) actions_at,
-            (long int) preactions_at,
-            (long int) adjectives_offset,
-            (long int) dictionary_at,
-            (long int) Write_Code_At,
-            (long int) Write_Strings_At);
-            if (module_switch)
-                printf("%05lx Linking data\n",(long int) link_table_at);
-        }
-    }
-
     if (debugfile_switch)
     {   begin_writing_debug_sections();
         write_debug_section("abbreviations", 64);
@@ -1100,130 +1003,125 @@ Out:   Version %d \"%s\" %s %d.%c%c%c%c%c%c (%ld%sK long):\n",
         end_writing_debug_sections(Out_Size);
     }
 
-    if (memory_map_switch)
+    if (memory_map_setting)
     {
+        int32 addr;
         {
 printf("Dynamic +---------------------+   00000\n");
-printf("memory  |       header        |\n");
+printf("memory  |       header        |   %s\n",
+    show_percentage(0x40, Out_Size));
 printf("        +---------------------+   00040\n");
-printf("        |    abbreviations    |\n");
+printf("        |    abbreviations    |   %s\n",
+    show_percentage(abbrevs_at-0x40, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n", (long int) abbrevs_at);
-printf("        | abbreviations table |\n");
+printf("        | abbreviations table |   %s\n",
+    show_percentage(headerext_at-abbrevs_at, Out_Size));
 printf("        +---------------------+   %05lx\n", (long int) headerext_at);
-printf("        |  header extension   |\n");
+addr = (alphabet_modified ? charset_at : (zscii_defn_modified ? unicode_at : prop_defaults_at));
+printf("        |  header extension   |   %s\n",
+    show_percentage(addr-headerext_at, Out_Size));
             if (alphabet_modified)
             {
 printf("        + - - - - - - - - - - +   %05lx\n", (long int) charset_at);
-printf("        |   alphabets table   |\n");
+addr = (zscii_defn_modified ? unicode_at : prop_defaults_at);
+printf("        |   alphabets table   |   %s\n",
+    show_percentage(addr-charset_at, Out_Size));
             }
             if (zscii_defn_modified)
             {
 printf("        + - - - - - - - - - - +   %05lx\n", (long int) unicode_at);
-printf("        |    Unicode table    |\n");
+printf("        |    Unicode table    |   %s\n",
+    show_percentage(prop_defaults_at-unicode_at, Out_Size));
             }
 printf("        +---------------------+   %05lx\n",
                                           (long int) prop_defaults_at);
-printf("        |  property defaults  |\n");
+printf("        |  property defaults  |   %s\n",
+    show_percentage(object_tree_at-prop_defaults_at, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n", (long int) object_tree_at);
-printf("        |       objects       |\n");
+printf("        |       objects       |   %s\n",
+    show_percentage(object_props_at-object_tree_at, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n",
                                           (long int) object_props_at);
 printf("        | object short names, |\n");
-printf("        | common prop values  |\n");
+printf("        | common prop values  |   %s\n",
+    show_percentage(class_numbers_offset-object_props_at, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n",
                                           (long int) class_numbers_offset);
-printf("        | class numbers table |\n");
+printf("        | class numbers table |   %s\n",
+    show_percentage(identifier_names_offset-class_numbers_offset, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n",
                                           (long int) identifier_names_offset);
-printf("        | symbol names table  |\n");
+printf("        | symbol names table  |   %s\n",
+    show_percentage(individuals_offset-identifier_names_offset, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n",
                                           (long int) individuals_offset);
-printf("        | indiv prop values   |\n");
+printf("        | indiv prop values   |   %s\n",
+    show_percentage(globals_at-individuals_offset, Out_Size));
 printf("        +---------------------+   %05lx\n", (long int) globals_at);
-printf("        |  global variables   |\n");
+printf("        |  global variables   |   %s\n",
+    show_percentage(480, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n",
                                           ((long int) globals_at)+480L);
-printf("        |       arrays        |\n");
+printf("        |       arrays        |   %s\n",
+    show_percentage(grammar_table_at-(globals_at+480), Out_Size));
 printf("        +=====================+   %05lx\n",
                                           (long int) grammar_table_at);
-printf("Readable|    grammar table    |\n");
+printf("Readable|    grammar table    |   %s\n",
+    show_percentage(actions_at-grammar_table_at, Out_Size));
 printf("memory  + - - - - - - - - - - +   %05lx\n", (long int) actions_at);
-printf("        |       actions       |\n");
+printf("        |       actions       |   %s\n",
+    show_percentage(preactions_at-actions_at, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n", (long int) preactions_at);
-printf("        |   parsing routines  |\n");
+printf("        |   parsing routines  |   %s\n",
+    show_percentage(adjectives_offset-preactions_at, Out_Size));
 printf("        + - - - - - - - - - - +   %05lx\n",
                                           (long int) adjectives_offset);
-printf("        |     adjectives      |\n");
+printf("        |     adjectives      |   %s\n",
+    show_percentage(dictionary_at-adjectives_offset, Out_Size));
 printf("        +---------------------+   %05lx\n", (long int) dictionary_at);
-printf("        |     dictionary      |\n");
+addr = (module_switch ? map_of_module : (static_array_area_size ? static_arrays_at : Write_Code_At));
+printf("        |     dictionary      |   %s\n",
+    show_percentage(addr-dictionary_at, Out_Size));
 if (module_switch)
 {
 printf("        + - - - - - - - - - - +   %05lx\n",
                                           (long int) map_of_module);
-printf("        | map of module addrs |\n");
+addr = (static_array_area_size ? static_arrays_at : Write_Code_At);
+printf("        | map of module addrs |   %s\n",
+    show_percentage(addr-map_of_module, Out_Size));
 }
 if (static_array_area_size)
 {
 printf("        +---------------------+   %05lx\n", (long int) static_arrays_at);
-printf("        |    static arrays    |\n");
+printf("        |    static arrays    |   %s\n",
+    show_percentage(Write_Code_At-static_arrays_at, Out_Size));
 }
 printf("        +=====================+   %05lx\n", (long int) Write_Code_At);
-printf("Above   |       Z-code        |\n");
+printf("Above   |       Z-code        |   %s\n",
+    show_percentage(Write_Strings_At-Write_Code_At, Out_Size));
 printf("readable+---------------------+   %05lx\n",
                                           (long int) Write_Strings_At);
-printf("memory  |       strings       |\n");
+addr = (module_switch ? link_table_at : Out_Size);
+printf("memory  |       strings       |   %s\n",
+    show_percentage(addr-Write_Strings_At, Out_Size));
 if (module_switch)
 {
 printf("        +=====================+   %05lx\n", (long int) link_table_at);
-printf("        | module linking data |\n");
+printf("        | module linking data |   %s\n",
+    show_percentage(Out_Size-link_table_at, Out_Size));
 }
 printf("        +---------------------+   %05lx\n", (long int) Out_Size);
         }
     }
-    if (percentages_switch)
-    {   printf("Approximate percentage breakdown of %s:\n",
-                output_called);
-        percentage("Z-code",             code_length,Out_Size);
-        if (module_switch)
-            percentage("Linking data",   link_data_size,Out_Size);
-        percentage("Static strings",     strings_length,Out_Size);
-        percentage("Dictionary",         Write_Code_At-dictionary_at,Out_Size);
-        percentage("Objects",            globals_at-prop_defaults_at,Out_Size);
-        percentage("Globals",            grammar_table_at-globals_at,Out_Size);
-        percentage("Parsing tables",   dictionary_at-grammar_table_at,Out_Size);
-        percentage("Header and synonyms", prop_defaults_at,Out_Size);
-        percentage("Total of save area", grammar_table_at,Out_Size);
-        percentage("Total of text",      total_bytes_trans,Out_Size);
-    }
-    if (frequencies_switch)
-    {
-        {   printf("How frequently abbreviations were used, and roughly\n");
-            printf("how many bytes they saved:  ('_' denotes spaces)\n");
-            for (i=0; i<no_abbreviations; i++)
-            {   char abbrev_string[MAX_ABBREV_LENGTH];
-                strcpy(abbrev_string,
-                    (char *)abbreviations_at+i*MAX_ABBREV_LENGTH);
-                for (j=0; abbrev_string[j]!=0; j++)
-                    if (abbrev_string[j]==' ') abbrev_string[j]='_';
-                printf("%10s %5d/%5d   ",abbrev_string,abbrev_freqs[i],
-                    2*((abbrev_freqs[i]-1)*abbrev_quality[i])/3);
-                if ((i%3)==2) printf("\n");
-            }
-            if ((i%3)!=0) printf("\n");
-            if (no_abbreviations==0) printf("None were declared.\n");
-        }
-    }
 }
 
 static void construct_storyfile_g(void)
 {   uchar *p;
-    int32 i, j, k, l, mark, strings_length, limit;
+    int32 i, j, k, l, mark, strings_length;
     int32 globals_at, dictionary_at, actions_at, preactions_at,
           abbrevs_at, prop_defaults_at, object_tree_at, object_props_at,
-          grammar_table_at, charset_at, headerext_at,
-        unicode_at, arrays_at, static_arrays_at;
+          grammar_table_at, arrays_at, static_arrays_at;
     int32 threespaces, code_length;
-    char *output_called = (module_switch)?"module":"story file";
 
     ASSERT_GLULX();
 
@@ -1302,7 +1200,7 @@ static void construct_storyfile_g(void)
     }
 
     arrays_at = mark;
-    for (i=MAX_GLOBAL_VARIABLES*4; i<dynamic_array_area_size; i++)
+    for (i=0; i<dynamic_array_area_size; i++)
         p[mark++] = dynamic_array_area[i];
 
     /* -------------------------- Dynamic Strings -------------------------- */
@@ -1316,16 +1214,6 @@ static void construct_storyfile_g(void)
       mark += 4;
     }
 
-    /* ---------------- Various Things I'm Not Sure About ------------------ */
-    /* Actually, none of these are relevant to Glulx. */
-    headerext_at = mark;
-    charset_at = 0;
-    if (alphabet_modified)
-      charset_at = mark;
-    unicode_at = 0;
-    if (zscii_defn_modified)
-      unicode_at = mark;
-
     /*  -------------------- Objects and Properties ------------------------ */
 
     object_tree_at = mark;
@@ -1408,7 +1296,7 @@ static void construct_storyfile_g(void)
 
     prop_defaults_at = mark;
     for (i=0; i<no_properties; i++) {
-      k = prop_default_value[i];
+      k = commonprops[i].default_value;
       WriteInt32(p+mark, k);
       mark += 4;
     }
@@ -1418,7 +1306,7 @@ static void construct_storyfile_g(void)
     class_numbers_offset = mark;
     for (i=0; i<no_classes; i++) {
       j = Write_RAM_At + object_tree_at +
-        (OBJECT_BYTE_LENGTH*(class_object_numbers[i]-1));
+        (OBJECT_BYTE_LENGTH*(class_info[i].object_number-1));
       WriteInt32(p+mark, j);
       mark += 4;
     }
@@ -1577,7 +1465,6 @@ table format requested (producing number 2 format instead)");
     RAM_Size = mark;
 
     Out_Size = Write_RAM_At + RAM_Size;
-    limit=1024*1024;
 
     /*  --------------------------- Offsets -------------------------------- */
 
@@ -1606,7 +1493,7 @@ table format requested (producing number 2 format instead)");
 
         mark = actions_at + 4;
         for (i=0; i<no_actions; i++) {
-          j = action_byte_offset[i];
+          j = actions[i].byte_offset;
           if (OMIT_UNUSED_ROUTINES)
             j = df_stripped_address_for_address(j);
           j += code_offset;
@@ -1648,118 +1535,6 @@ table format requested (producing number 2 format instead)");
 
     /*  ---- From here on, it's all reportage: construction is finished ---- */
 
-    if (statistics_switch)
-    {   int32 k_long, rate; char *k_str="";
-        k_long=(Out_Size/1024);
-        if ((Out_Size-1024*k_long) >= 512) { k_long++; k_str=""; }
-        else if ((Out_Size-1024*k_long) > 0) { k_str=".5"; }
-        if (strings_length == 0) rate = 0;
-        else rate=strings_length*1000/total_chars_trans;
-
-        {   printf("In:\
-%3d source code files            %6d syntactic lines\n\
-%6d textual lines              %8ld characters ",
-            total_input_files, no_syntax_lines,
-            total_source_line_count, (long int) total_chars_read);
-            if (character_set_unicode) printf("(UTF-8)\n");
-            else if (character_set_setting == 0) printf("(plain ASCII)\n");
-            else
-            {   printf("(ISO 8859-%d %s)\n", character_set_setting,
-                    name_of_iso_set(character_set_setting));
-            }
-
-            {char serialnum[8];
-            write_serial_number(serialnum);
-            printf("Allocated:\n\
-%6d symbols (maximum %4d)    %8ld bytes of memory\n\
-Out:   %s %s %d.%c%c%c%c%c%c (%ld%sK long):\n",
-                 no_symbols, MAX_SYMBOLS,
-                 (long int) malloced_bytes,
-                 version_name(version_number),
-                 output_called,
-                 release_number,
-                 serialnum[0], serialnum[1], serialnum[2],
-                 serialnum[3], serialnum[4], serialnum[5],
-                 (long int) k_long, k_str);
-            } 
-
-            printf("\
-%6d classes (maximum %3d)        %6d objects (maximum %3d)\n\
-%6d global vars (maximum %3d)    %6d variable/array space (maximum %d)\n",
-                 no_classes, MAX_CLASSES,
-                 no_objects, MAX_OBJECTS,
-                 no_globals, MAX_GLOBAL_VARIABLES,
-                 dynamic_array_area_size, MAX_STATIC_DATA);
-
-            printf(
-"%6d verbs (maximum %3d)          %6d dictionary entries (maximum %d)\n\
-%6d grammar lines (version %d)    %6d grammar tokens (unlimited)\n\
-%6d actions (maximum %3d)        %6d attributes (maximum %2d)\n\
-%6d common props (maximum %3d)   %6d individual props (unlimited)\n",
-                 no_Inform_verbs, MAX_VERBS,
-                 dict_entries, MAX_DICT_ENTRIES,
-                 no_grammar_lines, grammar_version_number,
-                 no_grammar_tokens,
-                 no_actions, MAX_ACTIONS,
-                 no_attributes, NUM_ATTR_BYTES*8,
-                 no_properties, INDIV_PROP_START,
-                 no_individual_properties - INDIV_PROP_START);
-
-            if (track_unused_routines)
-            {
-                uint32 diff = df_total_size_before_stripping - df_total_size_after_stripping;
-                printf(
-"%6ld bytes of code                %6ld unused bytes %s (%.1f%%)\n",
-                 (long int) df_total_size_before_stripping, (long int) diff,
-                 (OMIT_UNUSED_ROUTINES ? "stripped out" : "detected"),
-                 100 * (float)diff / (float)df_total_size_before_stripping);
-            }
-
-            printf(
-"%6ld characters used in text      %6ld bytes compressed (rate %d.%3ld)\n\
-%6d abbreviations (maximum %d)   %6d routines (unlimited)\n\
-%6ld instructions of code         %6d sequence points\n\
-%6ld bytes writable memory used   %6ld bytes read-only memory used\n\
-%6ld bytes used in machine    %10ld bytes free in machine\n",
-                 (long int) total_chars_trans,
-                 (long int) strings_length,
-                 (total_chars_trans>strings_length)?0:1,
-                 (long int) rate,
-                 no_abbreviations, MAX_ABBREVS,
-                 no_routines,
-                 (long int) no_instructions, no_sequence_points,
-                 (long int) (Out_Size - Write_RAM_At),
-                 (long int) Write_RAM_At,
-                 (long int) Out_Size,
-                 (long int)
-                      (((long int) (limit*1024L)) - ((long int) Out_Size)));
-
-        }
-    }
-
-    if (offsets_switch)
-    {
-        {   printf(
-"\nOffsets in %s:\n\
-%05lx Synonyms     %05lx Defaults     %05lx Objects    %05lx Properties\n\
-%05lx Variables    %05lx Parse table  %05lx Actions    %05lx Preactions\n\
-%05lx Adjectives   %05lx Dictionary   %05lx Code       %05lx Strings\n",
-            output_called,
-            (long int) abbrevs_at,
-            (long int) prop_defaults_at,
-            (long int) object_tree_at,
-            (long int) object_props_at,
-            (long int) globals_at,
-            (long int) grammar_table_at,
-            (long int) actions_at,
-            (long int) preactions_at,
-            (long int) adjectives_offset,
-            (long int) dictionary_at,
-            (long int) Write_Code_At,
-            (long int) Write_Strings_At);
-        }
-    }
-
     if (debugfile_switch)
     {   begin_writing_debug_sections();
         write_debug_section("memory layout id", GLULX_HEADER_SIZE);
@@ -1797,73 +1572,79 @@ Out:   %s %s %d.%c%c%c%c%c%c (%ld%sK long):\n",
         end_writing_debug_sections(Out_Size + MEMORY_MAP_EXTENSION);
     }
 
-    if (memory_map_switch)
+    if (memory_map_setting)
     {
-
+        int32 addr;
         {
 printf("        +---------------------+   000000\n");
-printf("Read-   |       header        |\n");
+printf("Read-   |       header        |   %s\n",
+    show_percentage(GLULX_HEADER_SIZE, Out_Size));
 printf(" only   +=====================+   %06lx\n", (long int) GLULX_HEADER_SIZE);
-printf("memory  |  memory layout id   |\n");
+printf("memory  |  memory layout id   |   %s\n",
+    show_percentage(Write_Code_At-GLULX_HEADER_SIZE, Out_Size));
 printf("        +---------------------+   %06lx\n", (long int) Write_Code_At);
-printf("        |        code         |\n");
+printf("        |        code         |   %s\n",
+    show_percentage(Write_Strings_At-Write_Code_At, Out_Size));
 printf("        +---------------------+   %06lx\n",
   (long int) Write_Strings_At);
-printf("        | string decode table |\n");
+printf("        | string decode table |   %s\n",
+    show_percentage(compression_table_size, Out_Size));
 printf("        + - - - - - - - - - - +   %06lx\n",
   (long int) Write_Strings_At + compression_table_size);
-printf("        |       strings       |\n");
+addr = (static_array_area_size ? static_arrays_at : Write_RAM_At+globals_at);
+printf("        |       strings       |   %s\n",
+    show_percentage(addr-(Write_Strings_At + compression_table_size), Out_Size));
             if (static_array_area_size)
             {
 printf("        +---------------------+   %06lx\n", 
   (long int) (static_arrays_at));
-printf("        |    static arrays    |\n");
+printf("        |    static arrays    |   %s\n",
+    show_percentage(Write_RAM_At+globals_at-static_arrays_at, Out_Size));
             }
 printf("        +=====================+   %06lx\n", 
   (long int) (Write_RAM_At+globals_at));
-printf("Dynamic |  global variables   |\n");
+printf("Dynamic |  global variables   |   %s\n",
+    show_percentage(arrays_at-globals_at, Out_Size));
 printf("memory  + - - - - - - - - - - +   %06lx\n",
   (long int) (Write_RAM_At+arrays_at));
-printf("        |       arrays        |\n");
+printf("        |       arrays        |   %s\n",
+    show_percentage(abbrevs_at-arrays_at, Out_Size));
 printf("        +---------------------+   %06lx\n",
   (long int) (Write_RAM_At+abbrevs_at));
-printf("        | printing variables  |\n");
-            if (alphabet_modified)
-            {
-printf("        + - - - - - - - - - - +   %06lx\n", 
-  (long int) (Write_RAM_At+charset_at));
-printf("        |   alphabets table   |\n");
-            }
-            if (zscii_defn_modified)
-            {
-printf("        + - - - - - - - - - - +   %06lx\n", 
-  (long int) (Write_RAM_At+unicode_at));
-printf("        |    Unicode table    |\n");
-            }
+printf("        | printing variables  |   %s\n",
+    show_percentage(object_tree_at-abbrevs_at, Out_Size));
 printf("        +---------------------+   %06lx\n", 
   (long int) (Write_RAM_At+object_tree_at));
-printf("        |       objects       |\n");
+printf("        |       objects       |   %s\n",
+    show_percentage(object_props_at-object_tree_at, Out_Size));
 printf("        + - - - - - - - - - - +   %06lx\n",
   (long int) (Write_RAM_At+object_props_at));
-printf("        |   property values   |\n");
+printf("        |   property values   |   %s\n",
+    show_percentage(prop_defaults_at-object_props_at, Out_Size));
 printf("        + - - - - - - - - - - +   %06lx\n",
   (long int) (Write_RAM_At+prop_defaults_at));
-printf("        |  property defaults  |\n");
+printf("        |  property defaults  |   %s\n",
+    show_percentage(class_numbers_offset-prop_defaults_at, Out_Size));
 printf("        + - - - - - - - - - - +   %06lx\n",
   (long int) (Write_RAM_At+class_numbers_offset));
-printf("        | class numbers table |\n");
+printf("        | class numbers table |   %s\n",
+    show_percentage(identifier_names_offset-class_numbers_offset, Out_Size));
 printf("        + - - - - - - - - - - +   %06lx\n",
   (long int) (Write_RAM_At+identifier_names_offset));
-printf("        |   id names table    |\n");
+printf("        |   id names table    |   %s\n",
+    show_percentage(grammar_table_at-identifier_names_offset, Out_Size));
 printf("        +---------------------+   %06lx\n",
   (long int) (Write_RAM_At+grammar_table_at));
-printf("        |    grammar table    |\n");
+printf("        |    grammar table    |   %s\n",
+    show_percentage(actions_at-grammar_table_at, Out_Size));
 printf("        + - - - - - - - - - - +   %06lx\n", 
   (long int) (Write_RAM_At+actions_at));
-printf("        |       actions       |\n");
+printf("        |       actions       |   %s\n",
+    show_percentage(dictionary_offset-(Write_RAM_At+actions_at), Out_Size));
 printf("        +---------------------+   %06lx\n", 
   (long int) dictionary_offset);
-printf("        |     dictionary      |\n");
+printf("        |     dictionary      |   %s\n",
+    show_percentage(Out_Size-dictionary_offset, Out_Size));
             if (MEMORY_MAP_EXTENSION == 0)
             {
 printf("        +---------------------+   %06lx\n", (long int) Out_Size);
@@ -1871,56 +1652,276 @@ printf("        +---------------------+   %06lx\n", (long int) Out_Size);
             else
             {
 printf("        +=====================+   %06lx\n", (long int) Out_Size);
-printf("Runtime |       (empty)       |\n");
+printf("Runtime |       (empty)       |\n");   /* no percentage */
 printf("  extn  +---------------------+   %06lx\n", (long int) Out_Size+MEMORY_MAP_EXTENSION);
             }
 
         }
 
     }
+}
 
+static void display_frequencies()
+{
+    int i, j;
+    
+    printf("How frequently abbreviations were used, and roughly\n");
+    printf("how many bytes they saved:  ('_' denotes spaces)\n");
+    
+    for (i=0; i<no_abbreviations; i++) {
+        int32 saving;
+        if (!glulx_mode)
+            saving = 2*((abbreviations[i].freq-1)*abbreviations[i].quality)/3;
+        else
+            saving = (abbreviations[i].freq-1)*abbreviations[i].quality;
+        
+        char abbrev_string[MAX_ABBREV_LENGTH];
+        strcpy(abbrev_string,
+               (char *)abbreviations_at+i*MAX_ABBREV_LENGTH);
+        for (j=0; abbrev_string[j]!=0; j++)
+            if (abbrev_string[j]==' ') abbrev_string[j]='_';
+        
+        printf("%10s %5d/%5d   ",abbrev_string,abbreviations[i].freq, saving);
+        
+        if ((i%3)==2) printf("\n");
+    }
+    if ((i%3)!=0) printf("\n");
+    
+    if (no_abbreviations==0) printf("None were declared.\n");
+}
 
-    if (percentages_switch)
-    {   printf("Approximate percentage breakdown of %s:\n",
-                output_called);
-        percentage("Code",               code_length,Out_Size);
-        if (module_switch)
-            percentage("Linking data",   link_data_size,Out_Size);
-        percentage("Static strings",     strings_length,Out_Size);
-        percentage("Dictionary",         Write_Code_At-dictionary_at,Out_Size);
-        percentage("Objects",            globals_at-prop_defaults_at,Out_Size);
-        percentage("Globals",            grammar_table_at-globals_at,Out_Size);
-        percentage("Parsing tables",   dictionary_at-grammar_table_at,Out_Size);
-        percentage("Header and synonyms", prop_defaults_at,Out_Size);
-        percentage("Total of save area", grammar_table_at,Out_Size);
-        percentage("Total of text",      strings_length,Out_Size);
-    }
-    if (frequencies_switch)
-    {
-        {   printf("How frequently abbreviations were used, and roughly\n");
-            printf("how many bytes they saved:  ('_' denotes spaces)\n");
-            for (i=0; i<no_abbreviations; i++)
-            {   char abbrev_string[MAX_ABBREV_LENGTH];
-                strcpy(abbrev_string,
-                    (char *)abbreviations_at+i*MAX_ABBREV_LENGTH);
-                for (j=0; abbrev_string[j]!=0; j++)
-                    if (abbrev_string[j]==' ') abbrev_string[j]='_';
-                printf("%10s %5d/%5d   ",abbrev_string,abbrev_freqs[i],
-                    2*((abbrev_freqs[i]-1)*abbrev_quality[i])/3);
-                if ((i%3)==2) printf("\n");
+static void display_statistics_z()
+{
+    int32 k_long, rate;
+    char *k_str = "";
+    uchar *p = (uchar *) zmachine_paged_memory;
+    char *output_called = (module_switch)?"module":"story file";
+    int limit = 0;
+
+    /* Yeah, we're repeating this calculation from construct_storyfile_z() */
+    switch(version_number)
+    {   case 3: limit = 128; break;
+        case 4:
+        case 5: limit = 256; break;
+        case 6:
+        case 7:
+        case 8: limit = 512; break;
+    }
+
+    k_long=(Out_Size/1024);
+    if ((Out_Size-1024*k_long) >= 512) { k_long++; k_str=""; }
+    else if ((Out_Size-1024*k_long) > 0) { k_str=".5"; }
+    if (total_bytes_trans == 0) rate = 0;
+    else rate=total_bytes_trans*1000/total_chars_trans;
+    
+    {   printf("In:\
+%3d source code files            %6d syntactic lines\n\
+%6d textual lines              %8ld characters ",
+               total_input_files, no_syntax_lines,
+               total_source_line_count, (long int) total_chars_read);
+        if (character_set_unicode) printf("(UTF-8)\n");
+        else if (character_set_setting == 0) printf("(plain ASCII)\n");
+        else
+            {   printf("(ISO 8859-%d %s)\n", character_set_setting,
+                       name_of_iso_set(character_set_setting));
             }
-            if ((i%3)!=0) printf("\n");
-            if (no_abbreviations==0) printf("None were declared.\n");
-        }
+
+        printf("Allocated:\n\
+%6d symbols                    %8ld bytes of memory\n\
+Out:   Version %d \"%s\" %s %d.%c%c%c%c%c%c (%ld%sK long):\n",
+               no_symbols,
+               (long int) malloced_bytes,
+               version_number,
+               version_name(version_number),
+               output_called,
+               release_number, p[18], p[19], p[20], p[21], p[22], p[23],
+               (long int) k_long, k_str);
+
+        printf("\
+%6d classes                      %6d objects\n\
+%6d global vars (maximum 233)    %6d variable/array space\n",
+               no_classes,
+               no_objects,
+               no_globals,
+               dynamic_array_area_size);
+
+        printf(
+               "%6d verbs                        %6d dictionary entries\n\
+%6d grammar lines (version %d)    %6d grammar tokens (unlimited)\n\
+%6d actions                      %6d attributes (maximum %2d)\n\
+%6d common props (maximum %2d)    %6d individual props (unlimited)\n",
+               no_Inform_verbs,
+               dict_entries,
+               no_grammar_lines, grammar_version_number,
+               no_grammar_tokens,
+               no_actions,
+               no_attributes, ((version_number==3)?32:48),
+               no_properties-3, ((version_number==3)?29:61),
+               no_individual_properties - 64);
+
+        if (track_unused_routines)
+            {
+                uint32 diff = df_total_size_before_stripping - df_total_size_after_stripping;
+                printf(
+                       "%6ld bytes of Z-code              %6ld unused bytes %s (%.1f%%)\n",
+                       (long int) df_total_size_before_stripping, (long int) diff,
+                       (OMIT_UNUSED_ROUTINES ? "stripped out" : "detected"),
+                       100 * (float)diff / (float)df_total_size_before_stripping);
+            }
+
+        printf(
+               "%6ld characters used in text      %6ld bytes compressed (rate %d.%3ld)\n\
+%6d abbreviations (maximum %d)   %6d routines (unlimited)\n\
+%6ld instructions of Z-code       %6d sequence points\n\
+%6ld bytes readable memory used (maximum 65536)\n\
+%6ld bytes used in Z-machine      %6ld bytes free in Z-machine\n",
+               (long int) total_chars_trans,
+               (long int) total_bytes_trans,
+               (total_chars_trans>total_bytes_trans)?0:1,
+               (long int) rate,
+               no_abbreviations, MAX_ABBREVS,
+               no_routines,
+               (long int) no_instructions, no_sequence_points,
+               (long int) Write_Code_At,
+               (long int) Out_Size,
+               (long int)
+               (((long int) (limit*1024L)) - ((long int) Out_Size)));
+
+    }
+}
+
+static void display_statistics_g()
+{
+    int32 k_long, rate;
+    char *k_str = "";
+    int32 limit = 1024*1024;
+    int32 strings_length = compression_table_size + compression_string_size;
+    char *output_called = (module_switch)?"module":"story file";
+    
+    k_long=(Out_Size/1024);
+    if ((Out_Size-1024*k_long) >= 512) { k_long++; k_str=""; }
+    else if ((Out_Size-1024*k_long) > 0) { k_str=".5"; }
+    
+    if (strings_length == 0) rate = 0;
+    else rate=strings_length*1000/total_chars_trans;
+
+    {   printf("In:\
+%3d source code files            %6d syntactic lines\n\
+%6d textual lines              %8ld characters ",
+               total_input_files, no_syntax_lines,
+               total_source_line_count, (long int) total_chars_read);
+        if (character_set_unicode) printf("(UTF-8)\n");
+        else if (character_set_setting == 0) printf("(plain ASCII)\n");
+        else
+            {   printf("(ISO 8859-%d %s)\n", character_set_setting,
+                       name_of_iso_set(character_set_setting));
+            }
+
+        {char serialnum[8];
+            write_serial_number(serialnum);
+            printf("Allocated:\n\
+%6d symbols                    %8ld bytes of memory\n\
+Out:   %s %s %d.%c%c%c%c%c%c (%ld%sK long):\n",
+                   no_symbols,
+                   (long int) malloced_bytes,
+                   version_name(version_number),
+                   output_called,
+                   release_number,
+                   serialnum[0], serialnum[1], serialnum[2],
+                   serialnum[3], serialnum[4], serialnum[5],
+                   (long int) k_long, k_str);
+        } 
+
+        printf("\
+%6d classes                      %6d objects\n\
+%6d global vars                  %6d variable/array space\n",
+               no_classes,
+               no_objects,
+               no_globals,
+               dynamic_array_area_size);
+
+        printf(
+               "%6d verbs                        %6d dictionary entries\n\
+%6d grammar lines (version %d)    %6d grammar tokens (unlimited)\n\
+%6d actions                      %6d attributes (maximum %2d)\n\
+%6d common props (maximum %3d)   %6d individual props (unlimited)\n",
+               no_Inform_verbs,
+               dict_entries,
+               no_grammar_lines, grammar_version_number,
+               no_grammar_tokens,
+               no_actions,
+               no_attributes, NUM_ATTR_BYTES*8,
+               no_properties-3, INDIV_PROP_START-3,
+               no_individual_properties - INDIV_PROP_START);
+
+        if (track_unused_routines)
+            {
+                uint32 diff = df_total_size_before_stripping - df_total_size_after_stripping;
+                printf(
+                       "%6ld bytes of code                %6ld unused bytes %s (%.1f%%)\n",
+                       (long int) df_total_size_before_stripping, (long int) diff,
+                       (OMIT_UNUSED_ROUTINES ? "stripped out" : "detected"),
+                       100 * (float)diff / (float)df_total_size_before_stripping);
+            }
+
+        printf(
+               "%6ld characters used in text      %6ld bytes compressed (rate %d.%3ld)\n\
+%6d abbreviations (maximum %d)   %6d routines (unlimited)\n\
+%6ld instructions of code         %6d sequence points\n\
+%6ld bytes writable memory used   %6ld bytes read-only memory used\n\
+%6ld bytes used in machine    %10ld bytes free in machine\n",
+               (long int) total_chars_trans,
+               (long int) strings_length,
+               (total_chars_trans>strings_length)?0:1,
+               (long int) rate,
+               no_abbreviations, MAX_ABBREVS,
+               no_routines,
+               (long int) no_instructions, no_sequence_points,
+               (long int) (Out_Size - Write_RAM_At),
+               (long int) Write_RAM_At,
+               (long int) Out_Size,
+               (long int)
+               (((long int) (limit*1024L)) - ((long int) Out_Size)));
+
     }
 }
 
+
 extern void construct_storyfile(void)
 {
-  if (!glulx_mode)
-    construct_storyfile_z();
-  else
-    construct_storyfile_g();
+    if (!glulx_mode)
+        construct_storyfile_z();
+    else
+        construct_storyfile_g();
+
+    /* Display all the trace/stats info that came out of compilation.
+
+       (Except for the memory map, which uses a bunch of local variables
+       from construct_storyfile_z/g(), so it's easier to do that inside
+       that function.)
+    */
+    
+    if (frequencies_setting)
+        display_frequencies();
+
+    if (list_symbols_setting)
+        list_symbols(list_symbols_setting);
+    
+    if (list_dict_setting)
+        show_dictionary(list_dict_setting);
+    
+    if (list_verbs_setting)
+        list_verb_table();
+
+    if (list_objects_setting)
+        list_object_tree();
+    
+    if (statistics_switch) {
+        if (!glulx_mode)
+            display_statistics_z();
+        else
+            display_statistics_g();
+    }
 }
 
 /* ========================================================================= */
index 88ceaecba825ae552ad94f1f9fc53f69131dd06a..525ecec6da03a40df06bc9b28d51f9b25bc28cbc 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "text" : Text translation, the abbreviations optimiser, the dictionary  */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
 #include "header.h"
 
-uchar *low_strings, *low_strings_top;  /* Start and next free byte in the low
-                                          strings pool */
+uchar *low_strings;                    /* Allocated to low_strings_top       */
+int32 low_strings_top;
+static memory_list low_strings_memlist;
 
 int32 static_strings_extent;           /* Number of bytes of static strings
                                           made so far */
-memory_block static_strings_area;      /* Used if (!temporary_files_switch) to
-                                          hold the static strings area so far */
+uchar *static_strings_area;            /* Used to hold the static strings
+                                          area so far
+                                          Allocated to static_strings_extent */
+memory_list static_strings_area_memlist;
 
-static uchar *strings_holding_area;    /* Area holding translated strings
-                                          until they are moved into either
-                                          a temporary file, or the
-                                          static_strings_area below */
-
-char *all_text, *all_text_top;         /* Start and next byte free in (large)
-                                          text buffer holding the entire text
+static char *all_text;                 /* Text buffer holding the entire text
                                           of the game, when it is being
-                                          recorded                           */
+                                          recorded
+                                          (Allocated to all_text_top) */
+static memory_list all_text_memlist;
+static int32 all_text_top;
+
 int abbrevs_lookup_table_made,         /* The abbreviations lookup table is
                                           constructed when the first non-
                                           abbreviation string is translated:
@@ -48,8 +49,6 @@ int abbrevs_lookup_table_made,         /* The abbreviations lookup table is
                                           with ASCII character n, or -1
                                           if none of the abbreviations do    */
 int no_abbreviations;                  /* No of abbreviations defined so far */
-uchar *abbreviations_at;                 /* Memory to hold the text of any
-                                          abbreviation strings declared      */
 /* ------------------------------------------------------------------------- */
 /*   Glulx string compression storage                                        */
 /* ------------------------------------------------------------------------- */
@@ -62,7 +61,6 @@ int no_dynamic_strings;                /* No. of @.. string escapes used
 int no_unicode_chars;                  /* Number of distinct Unicode chars
                                           used. (Beyond 0xFF.)               */
 
-static int MAX_CHARACTER_SET;          /* Number of possible entities */
 huffentity_t *huff_entities;           /* The list of entities (characters,
                                           abbreviations, @.. escapes, and 
                                           the terminator)                    */
@@ -87,11 +85,16 @@ int32 compression_string_size;         /* Length of the compressed string
 int32 *compressed_offsets;             /* The beginning of every string in
                                           the game, relative to the beginning
                                           of the Huffman table. (So entry 0
-                                          is equal to compression_table_size)*/
+                                          is equal to compression_table_size).
+                                          Allocated to no_strings at
+                                          compress_game_text() time.         */
+static memory_list compressed_offsets_memlist;
+
+unicode_usage_t *unicode_usage_entries; /* Allocated to no_unicode_chars     */
+static memory_list unicode_usage_entries_memlist;
 
 #define UNICODE_HASH_BUCKETS (64)
-unicode_usage_t *unicode_usage_entries;
-static unicode_usage_t *unicode_usage_hash[UNICODE_HASH_BUCKETS];
+static int unicode_usage_hash[UNICODE_HASH_BUCKETS];
 
 static int unicode_entity_index(int32 unicode);
 
@@ -99,9 +102,20 @@ static int unicode_entity_index(int32 unicode);
 /*   Abbreviation arrays                                                     */
 /* ------------------------------------------------------------------------- */
 
-int *abbrev_values;
-int *abbrev_quality;
-int *abbrev_freqs;
+abbreviation *abbreviations;             /* Allocated up to no_abbreviations */
+static memory_list abbreviations_memlist;
+
+/* Memory to hold the text of any abbreviation strings declared. This is
+   counted in units of MAX_ABBREV_LENGTH bytes. (An abbreviation must fit
+   in that many bytes, null included.)                                       */
+uchar *abbreviations_at;                 /* Allocated up to no_abbreviations */
+static memory_list abbreviations_at_memlist;
+
+static int *abbreviations_optimal_parse_schedule;
+static memory_list abbreviations_optimal_parse_schedule_memlist;
+
+static int *abbreviations_optimal_parse_scores;
+static memory_list abbreviations_optimal_parse_scores_memlist;
 
 /* ------------------------------------------------------------------------- */
 
@@ -110,26 +124,29 @@ int32 total_chars_trans,               /* Number of ASCII chars of text in   */
       zchars_trans_in_last_string;     /* Number of Z-chars in last string:
                                           needed only for abbrev efficiency
                                           calculation in "directs.c"         */
-static int32 total_zchars_trans,       /* Number of Z-chars of text out
+static int32 total_zchars_trans;       /* Number of Z-chars of text out
                                           (only used to calculate the above) */
-      no_chars_transcribed;            /* Number of ASCII chars written to
-                                          the text transcription area (used
-                                          for the -r and -u switches)        */
 
 static int zchars_out_buffer[3],       /* During text translation, a buffer of
                                           3 Z-chars at a time: when it's full
                                           these are written as a 2-byte word */
            zob_index;                  /* Index (0 to 2) into it             */
 
-static unsigned char *text_out_pc;     /* The "program counter" during text
-                                          translation: the next address to
+uchar *translated_text;                /* Area holding translated strings
+                                          until they are moved into the
+                                          static_strings_area below */
+static memory_list translated_text_memlist;
+
+static int32 text_out_pos;             /* The "program counter" during text
+                                          translation: the next position to
                                           write Z-coded text output to       */
 
-static unsigned char *text_out_limit;  /* The upper limit of text_out_pc
-                                          during text translation            */
+static int32 text_out_limit;           /* The upper limit of text_out_pos
+                                          during text translation (or -1
+                                          for no limit)                      */
 
 static int text_out_overflow;          /* During text translation, becomes
-                                          true if text_out_pc tries to pass
+                                          true if text_out_pos tries to pass
                                           text_out_limit                     */
 
 /* ------------------------------------------------------------------------- */
@@ -154,10 +171,10 @@ static void make_abbrevs_lookup(void)
                 p2=(char *)abbreviations_at+k*MAX_ABBREV_LENGTH;
                 if (strcmp(p1,p2)<0)
                 {   strcpy(p,p1); strcpy(p1,p2); strcpy(p2,p);
-                    l=abbrev_values[j]; abbrev_values[j]=abbrev_values[k];
-                    abbrev_values[k]=l;
-                    l=abbrev_quality[j]; abbrev_quality[j]=abbrev_quality[k];
-                    abbrev_quality[k]=l;
+                    l=abbreviations[j].value; abbreviations[j].value=abbreviations[k].value;
+                    abbreviations[k].value=l;
+                    l=abbreviations[j].quality; abbreviations[j].quality=abbreviations[k].quality;
+                    abbreviations[k].quality=l;
                     bubble_sort = TRUE;
                 }
             }
@@ -166,7 +183,7 @@ static void make_abbrevs_lookup(void)
     for (j=no_abbreviations-1; j>=0; j--)
     {   p1=(char *)abbreviations_at+j*MAX_ABBREV_LENGTH;
         abbrevs_lookup[(uchar)p1[0]]=j;
-        abbrev_freqs[j]=0;
+        abbreviations[j].freq=0;
     }
     abbrevs_lookup_table_made = TRUE;
 }
@@ -197,7 +214,7 @@ static int try_abbreviations_from(unsigned char *text, int i, int from)
             if (!glulx_mode) {
                 for (k=0; p[k]!=0; k++) text[i+k]=1;
             }
-            abbrev_freqs[j]++;
+            abbreviations[j].freq++;
             return(j);
             NotMatched: ;
         }
@@ -207,15 +224,25 @@ static int try_abbreviations_from(unsigned char *text, int i, int from)
 
 extern void make_abbreviation(char *text)
 {
+    ensure_memory_list_available(&abbreviations_memlist, no_abbreviations+1);
+    ensure_memory_list_available(&abbreviations_at_memlist, no_abbreviations+1);
+    
     strcpy((char *)abbreviations_at
             + no_abbreviations*MAX_ABBREV_LENGTH, text);
 
-    abbrev_values[no_abbreviations] = compile_string(text, STRCTX_ABBREV);
+    abbreviations[no_abbreviations].value = compile_string(text, STRCTX_ABBREV);
+    abbreviations[no_abbreviations].freq = 0;
 
     /*   The quality is the number of Z-chars saved by using this            */
     /*   abbreviation: note that it takes 2 Z-chars to print it.             */
 
-    abbrev_quality[no_abbreviations++] = zchars_trans_in_last_string - 2;
+    abbreviations[no_abbreviations].quality = zchars_trans_in_last_string - 2;
+
+    if (abbreviations[no_abbreviations].quality <= 0) {
+        warning_named("Abbreviation does not save any characters:", text);
+    }
+    
+    no_abbreviations++;
 }
 
 /* ------------------------------------------------------------------------- */
@@ -226,29 +253,47 @@ extern void make_abbreviation(char *text)
 /* ------------------------------------------------------------------------- */
 
 extern int32 compile_string(char *b, int strctx)
-{   int i, j; uchar *c;
-
+{   int32 i, j, k;
+    uchar *c;
+    int in_low_memory;
+
+    if (execution_never_reaches_here) {
+        /* No need to put strings into gametext.txt or the static/low
+           strings areas. */
+        if (strctx == STRCTX_GAME || strctx == STRCTX_GAMEOPC || strctx == STRCTX_LOWSTRING || strctx == STRCTX_INFIX) {
+            /* VENEER and VENEEROPC are only used at the translate_text level,
+               so we don't have to catch them here. */
+            return 0;
+        }
+    }
+    
     /* In Z-code, abbreviations go in the low memory pool (0x100). So
        do strings explicitly defined with the Lowstring directive.
        (In Glulx, the in_low_memory flag is ignored.) */
-    int in_low_memory = (strctx == STRCTX_ABBREV || strctx == STRCTX_LOWSTRING);
+    in_low_memory = (strctx == STRCTX_ABBREV || strctx == STRCTX_LOWSTRING);
 
     if (!glulx_mode && in_low_memory)
-    {   j=subtract_pointers(low_strings_top,low_strings);
-        low_strings_top=translate_text(low_strings_top, low_strings+MAX_LOW_STRINGS, b, strctx);
-        if (!low_strings_top)
-            memoryerror("MAX_LOW_STRINGS", MAX_LOW_STRINGS);
+    {
+        k = translate_text(-1, b, strctx);
+        if (k<0) {
+            error("text translation failed");
+            k = 0;
+        }
+        ensure_memory_list_available(&low_strings_memlist, low_strings_top+k);
+        memcpy(low_strings+low_strings_top, translated_text, k);
+        j = low_strings_top;
+        low_strings_top += k;
         return(0x21+(j/2));
     }
 
     if (glulx_mode && done_compression)
         compiler_error("Tried to add a string after compression was done.");
 
-    c = translate_text(strings_holding_area, strings_holding_area+MAX_STATIC_STRINGS, b, strctx);
-    if (!c)
-        memoryerror("MAX_STATIC_STRINGS",MAX_STATIC_STRINGS);
-
-    i = subtract_pointers(c, strings_holding_area);
+    i = translate_text(-1, b, strctx);
+    if (i < 0) {
+        error("text translation failed");
+        i = 0;
+    }
 
     /* Insert null bytes as needed to ensure that the next static string */
     /* also occurs at an address expressible as a packed address         */
@@ -261,23 +306,18 @@ extern int32 compile_string(char *b, int strctx)
             textalign = scale_factor;
         while ((i%textalign)!=0)
         {
-            if (i+2 > MAX_STATIC_STRINGS)
-                memoryerror("MAX_STATIC_STRINGS",MAX_STATIC_STRINGS);
-            i+=2; *c++ = 0; *c++ = 0;
+            ensure_memory_list_available(&translated_text_memlist, i+2);
+            translated_text[i++] = 0;
+            translated_text[i++] = 0;
         }
     }
 
     j = static_strings_extent;
 
-    if (temporary_files_switch)
-        for (c=strings_holding_area; c<strings_holding_area+i;
-             c++, static_strings_extent++)
-            fputc(*c,Temp1_fp);
-    else
-        for (c=strings_holding_area; c<strings_holding_area+i;
-             c++, static_strings_extent++)
-            write_byte_to_memory_block(&static_strings_area,
-                static_strings_extent, *c);
+    ensure_memory_list_available(&static_strings_area_memlist, static_strings_extent+i);
+    for (c=translated_text; c<translated_text+i;
+         c++, static_strings_extent++)
+        static_strings_area[static_strings_extent] = *c;
 
     if (!glulx_mode) {
         return(j/scale_factor);
@@ -302,11 +342,18 @@ static void write_z_char_z(int i)
     zob_index=0;
     j= zchars_out_buffer[0]*0x0400 + zchars_out_buffer[1]*0x0020
        + zchars_out_buffer[2];
-    if (text_out_pc+2 > text_out_limit) {
-        text_out_overflow = TRUE;
-        return;
+    
+    if (text_out_limit >= 0) {
+        if (text_out_pos+2 > text_out_limit) {
+            text_out_overflow = TRUE;
+            return;
+        }
     }
-    text_out_pc[0] = j/256; text_out_pc[1] = j%256; text_out_pc+=2;
+    else {
+        ensure_memory_list_available(&translated_text_memlist, text_out_pos+2);
+    }
+    
+    translated_text[text_out_pos++] = j/256; translated_text[text_out_pos++] = j%256;
     total_bytes_trans+=2;
 }
 
@@ -342,57 +389,84 @@ static void write_zscii(int zsc)
 /* ------------------------------------------------------------------------- */
 
 static void end_z_chars(void)
-{   unsigned char *p;
+{
     zchars_trans_in_last_string=total_zchars_trans-zchars_trans_in_last_string;
     while (zob_index!=0) write_z_char_z(5);
-    p=(unsigned char *) text_out_pc;
-    *(p-2)= *(p-2)+128;
+    if (text_out_pos < 2) {
+        /* Something went wrong. */
+        text_out_overflow = TRUE;
+        return;
+    }
+    translated_text[text_out_pos-2] += 128;
 }
 
 /* Glulx handles this much more simply -- compression is done elsewhere. */
 static void write_z_char_g(int i)
 {
-  ASSERT_GLULX();
-  if (text_out_pc+1 > text_out_limit) {
-      text_out_overflow = TRUE;
-      return;
-  }
-  total_zchars_trans++;
-  text_out_pc[0] = i;
-  text_out_pc++;
-  total_bytes_trans++;  
+    ASSERT_GLULX();
+    if (text_out_limit >= 0) {
+        if (text_out_pos+1 > text_out_limit) {
+            text_out_overflow = TRUE;
+            return;
+        }
+    }
+    else {
+        ensure_memory_list_available(&translated_text_memlist, text_out_pos+1);
+    }
+    total_zchars_trans++;
+    translated_text[text_out_pos++] = i;
+    total_bytes_trans++;  
+}
+
+/* Helper routine to compute the weight, in units, of a character handled by the Z-Machine */
+static int zchar_weight(int c)
+{
+    int lookup = iso_to_alphabet_grid[c];
+    if (lookup < 0) return 4;
+    if (lookup < 26) return 1;
+    return 2;
 }
 
 /* ------------------------------------------------------------------------- */
 /*   The main routine "text.c" provides to the rest of Inform: the text      */
-/*   translator. p is the address to write output to, s_text the source text */
-/*   and the return value is the next free address to write output to.       */
-/*   The return value will not exceed p_limit. If the translation tries to   */
-/*   overflow this boundary, the return value will be NULL (and you should   */
-/*   display an error).                                                      */
+/*   translator. s_text is the source text and the return value is the       */
+/*   number of bytes translated.                                             */
+/*   The translated text will be stored in translated_text.                  */
+/*                                                                           */
+/*   If p_limit is >= 0, the text length will not exceed that many bytes.    */
+/*   If the translation tries to overflow this boundary, the return value    */
+/*   will be -1. (You should display an error and not read translated_text.) */
+/*                                                                           */
+/*   If p_limit is negative, any amount of text is accepted (up to int32     */
+/*   anyway).                                                                */
+/*                                                                           */
 /*   Note that the source text may be corrupted by this routine.             */
 /* ------------------------------------------------------------------------- */
 
-extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx)
-{   int i, j, k, in_alphabet, lookup_value;
+extern int32 translate_text(int32 p_limit, char *s_text, int strctx)
+{   int i, j, k, in_alphabet, lookup_value, is_abbreviation;
     int32 unicode; int zscii;
     unsigned char *text_in;
 
+    if (p_limit >= 0) {
+        ensure_memory_list_available(&translated_text_memlist, p_limit);
+    }
+    
     /* For STRCTX_ABBREV, the string being translated is itself an
        abbreviation string, so it can't make use of abbreviations. Set
        the is_abbreviation flag to indicate this.
        The compiler has historically set this flag for the Lowstring
        directive as well -- the in_low_memory and is_abbreviation flag were
        always the same. I am preserving that convention. */
-    int is_abbreviation = (strctx == STRCTX_ABBREV || strctx == STRCTX_LOWSTRING);
+    is_abbreviation = (strctx == STRCTX_ABBREV || strctx == STRCTX_LOWSTRING);
 
 
-    /*  Cast the input and output streams to unsigned char: text_out_pc will
+    /*  Cast the input and output streams to unsigned char: text_out_pos will
         advance as bytes of Z-coded text are written, but text_in doesn't    */
 
     text_in     = (unsigned char *) s_text;
-    text_out_pc = (unsigned char *) p;
-    text_out_limit = (unsigned char *) p_limit;
+    text_out_pos = 0;
+    text_out_limit = p_limit;
     text_out_overflow = FALSE;
 
     /*  Remember the Z-chars total so that later we can subtract to find the
@@ -415,14 +489,17 @@ extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx)
         && (!is_abbreviation))
         make_abbrevs_lookup();
 
-    /*  If we're storing the whole game text to memory, then add this text   */
+    /*  If we're storing the whole game text to memory, then add this text.
+        We will put two newlines between each text and four at the very end.
+        (The optimise code does a lot of sloppy text[i+2], so the extra
+        two newlines past all_text_top are necessary.) */
 
     if ((!is_abbreviation) && (store_the_text))
-    {   no_chars_transcribed += strlen(s_text)+2;
-        if (no_chars_transcribed >= MAX_TRANSCRIPT_SIZE)
-            memoryerror("MAX_TRANSCRIPT_SIZE", MAX_TRANSCRIPT_SIZE);
-        sprintf(all_text_top, "%s\n\n", s_text);
-        all_text_top += strlen(all_text_top);
+    {   int addlen = strlen(s_text);
+        ensure_memory_list_available(&all_text_memlist, all_text_top+addlen+5);
+        sprintf(all_text+all_text_top, "%s\n\n\n\n", s_text);
+        /* Advance past two newlines. */
+        all_text_top += (addlen+2);
     }
 
     if (transcript_switch) {
@@ -439,6 +516,52 @@ extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx)
         }
     }
     
+    /* Computing the optimal way to parse strings to insert abbreviations with dynamic programming */
+    /*  (ref: R.A. Wagner , "Common phrases and minimum-space text storage", Commun. ACM, 16 (3) (1973)) */
+    /* We compute this optimal way here; it's stored in abbreviations_optimal_parse_schedule */
+    if (economy_switch)
+    {   
+        uchar *q, c;
+        int l, min_score, from;
+        int text_in_length;
+
+        text_in_length = strlen( (char*) text_in);
+        ensure_memory_list_available(&abbreviations_optimal_parse_schedule_memlist, text_in_length);
+        ensure_memory_list_available(&abbreviations_optimal_parse_scores_memlist, text_in_length+1);
+        
+        abbreviations_optimal_parse_scores[text_in_length] = 0;
+        for(j=text_in_length-1; j>=0; j--)
+        {   /* Initial values: empty schedule, score = just write the letter without abbreviating. */
+            abbreviations_optimal_parse_schedule[j] = -1;
+            min_score = zchar_weight(text_in[j]) + abbreviations_optimal_parse_scores[j+1];
+            /* If there's an abbreviation starting with that letter... */
+            if ( (from = abbrevs_lookup[text_in[j]]) != -1)
+            {
+                c = text_in[j];
+                /* Loop on all abbreviations starting with what is in c. */
+                for (k=from, q=(uchar *)abbreviations_at+from*MAX_ABBREV_LENGTH;
+                    (k<no_abbreviations)&&(c==q[0]); k++, q+=MAX_ABBREV_LENGTH)
+                {   
+                    /* Let's compare; we also keep track of the length of the abbreviation. */
+                    for (l=1; q[l]!=0; l++)
+                    {    if (text_in[j+l]!=q[l]) {goto NotMatched;}
+                    }
+                    /* We have a match (length l), but is it smaller in size? */
+                    if (min_score > 2 + abbreviations_optimal_parse_scores[j+l])
+                    {   /* It is indeed smaller, so let's write it down in our schedule. */
+                        min_score = 2 + abbreviations_optimal_parse_scores[j+l];
+                        abbreviations_optimal_parse_schedule[j] = k;
+                    }
+                    NotMatched: ;
+                }
+            }
+            /* We gave it our best, this is the smallest we got. */
+            abbreviations_optimal_parse_scores[j] = min_score;
+        }
+    }
+
+
+    
   if (!glulx_mode) {
 
     /*  The empty string of Z-text is illegal, since it can't carry an end
@@ -466,16 +589,24 @@ extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx)
             }
         }
 
-        /*  Try abbreviations if the economy switch set                      */
-
-        if ((economy_switch) && (!is_abbreviation)
-            && ((k=abbrevs_lookup[text_in[i]])!=-1))
-        {   if ((j=try_abbreviations_from(text_in, i, k))!=-1)
-            {   /* abbreviations run from MAX_DYNAMIC_STRINGS to 96 */
-                j += MAX_DYNAMIC_STRINGS;
-                write_z_char_z(j/32+1); write_z_char_z(j%32);
-            }
+        /*  Try abbreviations if the economy switch set. */
+        /*  Look at the abbreviation schedule to see if we should abbreviate here. */
+        /*  Note: Just because the schedule has something doesn't mean we should abbreviate there; */
+        /*  sometimes you abbreviate before because it's better. If we have already replaced the */
+        /*  char by a '1', it means we're in the middle of an abbreviation; don't try to abbreviate then. */
+        if ((economy_switch) && (!is_abbreviation) && text_in[i] != 1 &&
+            ((j = abbreviations_optimal_parse_schedule[i]) != -1))
+        {
+            /* Fill with 1s, which will get ignored by everyone else. */
+            uchar *p = (uchar *)abbreviations_at+j*MAX_ABBREV_LENGTH;
+            for (k=0; p[k]!=0; k++) text_in[i+k]=1;
+            /* Actually write the abbreviation in the story file. */
+            abbreviations[j].freq++;
+            /* Abbreviations run from MAX_DYNAMIC_STRINGS to 96. */
+            j += MAX_DYNAMIC_STRINGS;
+            write_z_char_z(j/32+1); write_z_char_z(j%32);
         }
+        
 
         /* If Unicode switch set, use text_to_unicode to perform UTF-8
            decoding */
@@ -495,12 +626,11 @@ advance as part of 'Zcharacter table':", unicode);
         /*  '@' is the escape character in Inform string notation: the various
             possibilities are:
 
-                (printing only)
                 @@decimalnumber  :  write this ZSCII char (0 to 1023)
-                @twodigits       :  write the abbreviation string with this
-                                    decimal number
-
-                (any string context)
+                @twodigits or    :  write the abbreviation string with this
+                @(digits)           decimal number
+                @(symbol)        :  write the abbreviation string with this
+                                    (constant) value
                 @accentcode      :  this accented character: e.g.,
                                         for @'e write an E-acute
                 @{...}           :  this Unicode char (in hex)              */
@@ -508,7 +638,7 @@ advance as part of 'Zcharacter table':", unicode);
         if (text_in[i]=='@')
         {   if (text_in[i+1]=='@')
             {
-                /*   @@...   */
+                /*   @@... (ascii value)  */
 
                 i+=2; j=atoi((char *) (text_in+i));
                 switch(j)
@@ -526,6 +656,55 @@ advance as part of 'Zcharacter table':", unicode);
                 }
                 while (isdigit(text_in[i])) i++; i--;
             }
+            else if (text_in[i+1]=='(')
+            {
+                /*   @(...) (dynamic string)   */
+                char dsymbol[MAX_IDENTIFIER_LENGTH+1];
+                int len = 0, digits = 0;
+                i += 2;
+                /* This accepts "12xyz" as a symbol, which it really isn't,
+                   but that just means it won't be found. */
+                while ((text_in[i] == '_' || isalnum(text_in[i])) && len < MAX_IDENTIFIER_LENGTH) {
+                    char ch = text_in[i++];
+                    if (isdigit(ch)) digits++;
+                    dsymbol[len++] = ch;
+                }
+                dsymbol[len] = '\0';
+                j = -1;
+                /* We would like to parse dsymbol as *either* a decimal
+                   number or a constant symbol. */
+                if (text_in[i] != ')' || len == 0) {
+                    error("'@(...)' abbreviation must contain a symbol");
+                }
+                else if (digits == len) {
+                    /* all digits; parse as decimal */
+                    j = atoi(dsymbol);
+                }
+                else {
+                    int sym = symbol_index(dsymbol, -1);
+                    if ((symbols[sym].flags & UNKNOWN_SFLAG) || symbols[sym].type != CONSTANT_T || symbols[sym].marker) {
+                        error_named("'@(...)' abbreviation expected a known constant value, but contained", dsymbol);
+                    }
+                    else {
+                        symbols[sym].flags |= USED_SFLAG;
+                        j = symbols[sym].value;
+                    }
+                }
+                if (!glulx_mode && j >= 96) {
+                    error_max_dynamic_strings(j);
+                    j = -1;
+                }
+                if (j >= MAX_DYNAMIC_STRINGS) {
+                    error_max_dynamic_strings(j);
+                    j = -1;
+                }
+                if (j >= 0) {
+                    write_z_char_z(j/32+1); write_z_char_z(j%32);
+                }
+                else {
+                    write_z_char_z(' '); /* error fallback */
+                }
+            }
             else if (isdigit(text_in[i+1])!=0)
             {   int d1, d2;
 
@@ -538,16 +717,22 @@ advance as part of 'Zcharacter table':", unicode);
                 else
                 {
                     j = d1*10 + d2;
-                    if (!glulx_mode && j >= 96)
-                    {   error("Z-machine dynamic strings are limited to 96");
-                        j = 0;
+                    if (!glulx_mode && j >= 96) {
+                        error_max_dynamic_strings(j);
+                        j = -1;
                     }
                     if (j >= MAX_DYNAMIC_STRINGS) {
-                        memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS);
-                        j = 0;
+                        /* Shouldn't get here with two digits */
+                        error_max_dynamic_strings(j);
+                        j = -1;
                     }
                     i+=2;
-                    write_z_char_z(j/32+1); write_z_char_z(j%32);
+                    if (j >= 0) {
+                        write_z_char_z(j/32+1); write_z_char_z(j%32);
+                    }
+                    else {
+                        write_z_char_z(' '); /* error fallback */
+                    }
                 }
             }
             else
@@ -609,7 +794,6 @@ advance as part of 'Zcharacter table':", unicode);
     /*  Flush the Z-characters output buffer and set the "end" bit           */
 
     end_z_chars();
-
   }
   else {
 
@@ -673,6 +857,56 @@ string.");
           write_z_char_g(j);
           while (isdigit(text_in[i])) i++; i--;
         }
+        else if (text_in[i+1]=='(') {
+            char dsymbol[MAX_IDENTIFIER_LENGTH+1];
+            int len = 0, digits = 0;
+            i += 2;
+            /* This accepts "12xyz" as a symbol, which it really isn't,
+               but that just means it won't be found. */
+            while ((text_in[i] == '_' || isalnum(text_in[i])) && len < MAX_IDENTIFIER_LENGTH) {
+                char ch = text_in[i++];
+                if (isdigit(ch)) digits++;
+                dsymbol[len++] = ch;
+            }
+            dsymbol[len] = '\0';
+            j = -1;
+            /* We would like to parse dsymbol as *either* a decimal
+               number or a constant symbol. */
+            if (text_in[i] != ')' || len == 0) {
+                error("'@(...)' abbreviation must contain a symbol");
+            }
+            else if (digits == len) {
+                /* all digits; parse as decimal */
+                j = atoi(dsymbol);
+            }
+            else {
+                int sym = symbol_index(dsymbol, -1);
+                if ((symbols[sym].flags & UNKNOWN_SFLAG) || symbols[sym].type != CONSTANT_T || symbols[sym].marker) {
+                    error_named("'@(...)' abbreviation expected a known constant value, but contained", dsymbol);
+                }
+                else {
+                    symbols[sym].flags |= USED_SFLAG;
+                    j = symbols[sym].value;
+                }
+            }
+            if (j >= MAX_DYNAMIC_STRINGS) {
+                error_max_dynamic_strings(j);
+                j = -1;
+            }
+            if (j+1 >= no_dynamic_strings)
+                no_dynamic_strings = j+1;
+            if (j >= 0) {
+                write_z_char_g('@');
+                write_z_char_g('D');
+                write_z_char_g('A' + ((j >>12) & 0x0F));
+                write_z_char_g('A' + ((j >> 8) & 0x0F));
+                write_z_char_g('A' + ((j >> 4) & 0x0F));
+                write_z_char_g('A' + ((j     ) & 0x0F));
+            }
+            else {
+                write_z_char_g(' '); /* error fallback */
+            }
+        }
         else if (isdigit(text_in[i+1])) {
           int d1, d2;
           d1 = character_digit_value[text_in[i+1]];
@@ -687,17 +921,22 @@ string; substituting '   '.");
             i += 2;
             j = d1*10 + d2;
             if (j >= MAX_DYNAMIC_STRINGS) {
-              memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS);
-              j = 0;
+              error_max_dynamic_strings(j);
+              j = -1;
             }
             if (j+1 >= no_dynamic_strings)
               no_dynamic_strings = j+1;
-            write_z_char_g('@');
-            write_z_char_g('D');
-            write_z_char_g('A' + ((j >>12) & 0x0F));
-            write_z_char_g('A' + ((j >> 8) & 0x0F));
-            write_z_char_g('A' + ((j >> 4) & 0x0F));
-            write_z_char_g('A' + ((j     ) & 0x0F));
+            if (j >= 0) {
+                write_z_char_g('@');
+                write_z_char_g('D');
+                write_z_char_g('A' + ((j >>12) & 0x0F));
+                write_z_char_g('A' + ((j >> 8) & 0x0F));
+                write_z_char_g('A' + ((j >> 4) & 0x0F));
+                write_z_char_g('A' + ((j     ) & 0x0F));
+            }
+            else {
+                write_z_char_g(' '); /* error fallback */
+            }
           }
         }
         else {
@@ -784,41 +1023,31 @@ string; substituting '?'.");
       }
     }
     write_z_char_g(0);
+    zchars_trans_in_last_string=total_zchars_trans-zchars_trans_in_last_string;
 
   }
 
   if (text_out_overflow)
-      return NULL;
+      return -1;
   else
-      return((uchar *) text_out_pc);
+      return text_out_pos;
 }
 
 static int unicode_entity_index(int32 unicode)
 {
-  unicode_usage_t *uptr;
   int j;
   int buck = unicode % UNICODE_HASH_BUCKETS;
 
-  for (uptr = unicode_usage_hash[buck]; uptr; uptr=uptr->next) {
-    if (uptr->ch == unicode)
+  for (j = unicode_usage_hash[buck]; j >= 0; j=unicode_usage_entries[j].next) {
+    if (unicode_usage_entries[j].ch == unicode)
       break;
   }
-  if (uptr) {
-    j = (uptr - unicode_usage_entries);
-  }
-  else {
-    if (no_unicode_chars >= MAX_UNICODE_CHARS) {
-      memoryerror("MAX_UNICODE_CHARS", MAX_UNICODE_CHARS);
-      j = 0;
-    }
-    else {
-      j = no_unicode_chars;
-      no_unicode_chars++;
-      uptr = unicode_usage_entries + j;
-      uptr->ch = unicode;
-      uptr->next = unicode_usage_hash[buck];
-      unicode_usage_hash[buck] = uptr;
-    }
+  if (j < 0) {
+    ensure_memory_list_available(&unicode_usage_entries_memlist, no_unicode_chars+1);
+    j = no_unicode_chars++;
+    unicode_usage_entries[j].ch = unicode;
+    unicode_usage_entries[j].next = unicode_usage_hash[buck];
+    unicode_usage_hash[buck] = j;
   }
 
   return j;
@@ -841,9 +1070,16 @@ void compress_game_text()
   int jx;
   int ch;
   int32 ix;
+  int max_char_set;
   huffbitlist_t bits;
 
   if (compression_switch) {
+    max_char_set = 257 + no_abbreviations + no_dynamic_strings + no_unicode_chars;
+
+    huff_entities = my_calloc(sizeof(huffentity_t), max_char_set*2+1, 
+      "huffman entities");
+    hufflist = my_calloc(sizeof(huffentity_t *), max_char_set, 
+      "huffman node list");
 
     /* How many entities have we currently got? Well, 256 plus the
        string-terminator plus Unicode chars plus abbrevations plus
@@ -857,8 +1093,8 @@ void compress_game_text()
     huff_dynam_start = entities;
     entities += no_dynamic_strings;
 
-    if (entities > MAX_CHARACTER_SET)
-      memoryerror("MAX_CHARACTER_SET",MAX_CHARACTER_SET);
+    if (entities > max_char_set)
+      compiler_error("Too many entities for max_char_set");
 
     /* Characters */
     for (jx=0; jx<256; jx++) {
@@ -893,17 +1129,10 @@ void compress_game_text()
     no_huff_entities = 257;
     huff_unicode_start = 257;
     huff_abbrev_start = 257;
-    huff_dynam_start = 257+MAX_ABBREVS;
+    huff_dynam_start = 257+no_abbreviations;
     compression_table_size = 0;
   }
 
-  if (temporary_files_switch) {
-    fclose(Temp1_fp);
-    Temp1_fp=fopen(Temp1_Name,"rb");
-    if (Temp1_fp==NULL)
-      fatalerror("I/O failure: couldn't reopen temporary file 1");
-  }
-
   if (compression_switch) {
 
     for (lx=0, ix=0; lx<no_strings; lx++) {
@@ -911,10 +1140,7 @@ void compress_game_text()
       int done=FALSE;
       int32 escapeval=0;
       while (!done) {
-        if (temporary_files_switch)
-          ch = fgetc(Temp1_fp);
-        else
-          ch = read_byte_from_memory_block(&static_strings_area, ix);
+        ch = static_strings_area[ix];
         ix++;
         if (ix > static_strings_extent || ch < 0)
           compiler_error("Read too much not-yet-compressed text.");
@@ -1039,12 +1265,7 @@ void compress_game_text()
      without actually doing the compression. */
   compression_string_size = 0;
 
-  if (temporary_files_switch) {
-    fseek(Temp1_fp, 0, SEEK_SET);
-  }
-
-  if (no_strings >= MAX_NUM_STATIC_STRINGS) 
-    memoryerror("MAX_NUM_STATIC_STRINGS", MAX_NUM_STATIC_STRINGS);
+  ensure_memory_list_available(&compressed_offsets_memlist, no_strings);
 
   for (lx=0, ix=0; lx<no_strings; lx++) {
     int escapelen=0, escapetype=0;
@@ -1054,10 +1275,7 @@ void compress_game_text()
     compressed_offsets[lx] = compression_table_size + compression_string_size;
     compression_string_size++; /* for the type byte */
     while (!done) {
-      if (temporary_files_switch)
-        ch = fgetc(Temp1_fp);
-      else
-        ch = read_byte_from_memory_block(&static_strings_area, ix);
+      ch = static_strings_area[ix];
       ix++;
       if (ix > static_strings_extent || ch < 0)
         compiler_error("Read too much not-yet-compressed text.");
@@ -1182,11 +1400,16 @@ static void compress_makebits(int entnum, int depth, int prevbit,
 /*   for compatibility with previous releases.                               */
 /* ------------------------------------------------------------------------- */
 
+/* The complete game text. */
+static char *opttext;
+static int32 opttextlen;
+
 typedef struct tlb_s
 {   char text[4];
     int32 intab, occurrences;
 } tlb;
-static tlb *tlbtab;
+static tlb *tlbtab; /* Three-letter blocks (allocated up to no_occs) */
+static memory_list tlbtab_memlist;
 static int32 no_occs;
 
 static int32 *grandtable;
@@ -1198,16 +1421,19 @@ typedef struct optab_s
     int32  location;
     char text[MAX_ABBREV_LENGTH];
 } optab;
-static optab *bestyet, *bestyet2;
+static int32 MAX_BESTYET;
+static optab *bestyet; /* High-score entries (up to MAX_BESTYET used/allocated) */
+static optab *bestyet2; /* The selected entries (up to selected used; allocated to MAX_ABBREVS) */
 
 static int pass_no;
 
-static char *sub_buffer;
-
 static void optimise_pass(void)
-{   int32 i; int t1, t2;
+{
+    TIMEVALUE t1, t2;
+    float duration;
+    int32 i;
     int32 j, j2, k, nl, matches, noflags, score, min, minat=0, x, scrabble, c;
-    for (i=0; i<256; i++) bestyet[i].length=0;
+    for (i=0; i<MAX_BESTYET; i++) bestyet[i].length=0;
     for (i=0; i<no_occs; i++)
     {   if ((*(tlbtab[i].text)!=(int) '\n')&&(tlbtab[i].occurrences!=0))
         {
@@ -1215,24 +1441,24 @@ static void optimise_pass(void)
             if (i%((**g_pm_hndl).linespercheck) == 0)
             {   ProcessEvents (&g_proc);
                 if (g_proc != true)
-                {   free_arrays();
-                    if (store_the_text)
-                        my_free(&all_text,"transcription text");
+                {   ao_free_arrays();
                     longjmp (g_fallback, 1);
                 }
             }
 #endif
-            printf("Pass %d, %4ld/%ld '%s' (%ld occurrences) ",
-                pass_no, (long int) i, (long int) no_occs, tlbtab[i].text,
-                (long int) tlbtab[i].occurrences);
-            t1=(int) (time(0));
+            if (optabbrevs_trace_setting >= 2) {
+                printf("Pass %d, %4ld/%ld '%s' (%ld occurrences) ",
+                    pass_no, (long int) i, (long int) no_occs, tlbtab[i].text,
+                    (long int) tlbtab[i].occurrences);
+            }
+            TIMEVALUE_NOW(&t1);
             for (j=0; j<tlbtab[i].occurrences; j++)
             {   for (j2=0; j2<tlbtab[i].occurrences; j2++) grandflags[j2]=1;
                 nl=2; noflags=tlbtab[i].occurrences;
-                while ((noflags>=2)&&(nl<=62))
+                while ((noflags>=2)&&(nl<MAX_ABBREV_LENGTH-1))
                 {   nl++;
                     for (j2=0; j2<nl; j2++)
-                        if (all_text[grandtable[tlbtab[i].intab+j]+j2]=='\n')
+                        if (opttext[grandtable[tlbtab[i].intab+j]+j2]=='\n')
                             goto FinishEarly;
                     matches=0;
                     for (j2=j; j2<tlbtab[i].occurrences; j2++)
@@ -1240,8 +1466,8 @@ static void optimise_pass(void)
                         {   x=grandtable[tlbtab[i].intab+j2]
                               - grandtable[tlbtab[i].intab+j];
                          if (((x>-nl)&&(x<nl))
-                            || (memcmp(all_text+grandtable[tlbtab[i].intab+j],
-                                       all_text+grandtable[tlbtab[i].intab+j2],
+                            || (memcmp(opttext+grandtable[tlbtab[i].intab+j],
+                                       opttext+grandtable[tlbtab[i].intab+j2],
                                        nl)!=0))
                             {   grandflags[j2]=0; noflags--; }
                             else matches++;
@@ -1250,7 +1476,7 @@ static void optimise_pass(void)
                     scrabble=0;
                     for (k=0; k<nl; k++)
                     {   scrabble++;
-                        c=all_text[grandtable[tlbtab[i].intab+j+k]];
+                        c=opttext[grandtable[tlbtab[i].intab+j+k]];
                         if (c!=(int) ' ')
                         {   if (iso_to_alphabet_grid[c]<0)
                                 scrabble+=2;
@@ -1261,12 +1487,12 @@ static void optimise_pass(void)
                     }
                     score=(matches-1)*(scrabble-2);
                     min=score;
-                    for (j2=0; j2<256; j2++)
+                    for (j2=0; j2<MAX_BESTYET; j2++)
                     {   if ((nl==bestyet[j2].length)
-                                && (memcmp(all_text+bestyet[j2].location,
-                                       all_text+grandtable[tlbtab[i].intab+j],
+                                && (memcmp(opttext+bestyet[j2].location,
+                                       opttext+grandtable[tlbtab[i].intab+j],
                                        nl)==0))
-                        {   j2=256; min=score; }
+                        {   j2=MAX_BESTYET; min=score; }
                         else
                         {   if (bestyet[j2].score<min)
                             {   min=bestyet[j2].score; minat=j2;
@@ -1278,15 +1504,15 @@ static void optimise_pass(void)
                         bestyet[minat].length=nl;
                         bestyet[minat].location=grandtable[tlbtab[i].intab+j];
                         bestyet[minat].popularity=matches;
-                        for (j2=0; j2<nl; j2++) sub_buffer[j2]=
-                            all_text[bestyet[minat].location+j2];
-                        sub_buffer[nl]=0;
                     }
                 }
                 FinishEarly: ;
             }
-            t2=((int) time(0)) - t1;
-            printf(" (%d seconds)\n",t2);
+            if (optabbrevs_trace_setting >= 2) {
+                TIMEVALUE_NOW(&t2);
+                duration = TIMEVALUE_DIFFERENCE(&t1, &t2);
+                printf(" (%.4f seconds)\n", duration);
+            }
         }
     }
 }
@@ -1304,22 +1530,35 @@ static int any_overlap(char *s1, char *s2)
     return(0);
 }
 
-#define MAX_TLBS 8000
-
 extern void optimise_abbreviations(void)
-{   int32 i, j, t, max=0, MAX_GTABLE;
+{   int32 i, j, tcount, max=0, MAX_GTABLE;
     int32 j2, selected, available, maxat=0, nl;
-    tlb test;
 
+    if (opttext == NULL)
+        return;
+
+    /* We insist that the first two abbreviations will be ". " and ", ". */
+    if (MAX_ABBREVS < 2)
+        return;
+
+    /* Note that it's safe to access opttext[opttextlen+2]. There are
+       two newlines and a null beyond opttextlen. */
+    
     printf("Beginning calculation of optimal abbreviations...\n");
 
     pass_no = 0;
-    tlbtab=my_calloc(sizeof(tlb), MAX_TLBS, "tlb table"); no_occs=0;
-    sub_buffer=my_calloc(sizeof(char), 4000, "sub_buffer");
-    for (i=0; i<MAX_TLBS; i++) tlbtab[i].occurrences=0;
 
-    bestyet=my_calloc(sizeof(optab), 256, "bestyet");
-    bestyet2=my_calloc(sizeof(optab), 64, "bestyet2");
+    initialise_memory_list(&tlbtab_memlist,
+        sizeof(tlb), 1000, (void**)&tlbtab,
+        "three-letter-blocks buffer");
+    
+    no_occs=0;
+
+    /* Not sure what the optimal size is for MAX_BESTYET. The original code always created 64 abbreviations and used MAX_BESTYET=256. I'm guessing that 4*MAX_ABBREVS is reasonable. */
+    MAX_BESTYET = 4 * MAX_ABBREVS;
+    
+    bestyet=my_calloc(sizeof(optab), MAX_BESTYET, "bestyet");
+    bestyet2=my_calloc(sizeof(optab), MAX_ABBREVS, "bestyet2");
 
     bestyet2[0].text[0]='.';
     bestyet2[0].text[1]=' ';
@@ -1329,57 +1568,61 @@ extern void optimise_abbreviations(void)
     bestyet2[1].text[1]=' ';
     bestyet2[1].text[2]=0;
 
-    for (i=0; all_text+i<all_text_top; i++)
+    selected=2;
+
+    for (i=0; i<opttextlen; i++)
     {
-        if ((all_text[i]=='.') && (all_text[i+1]==' ') && (all_text[i+2]==' '))
-        {   all_text[i]='\n'; all_text[i+1]='\n'; all_text[i+2]='\n';
+        if ((opttext[i]=='.') && (opttext[i+1]==' ') && (opttext[i+2]==' '))
+        {   opttext[i]='\n'; opttext[i+1]='\n'; opttext[i+2]='\n';
             bestyet2[0].popularity++;
         }
 
-        if ((all_text[i]=='.') && (all_text[i+1]==' '))
-        {   all_text[i]='\n'; all_text[i+1]='\n';
+        if ((opttext[i]=='.') && (opttext[i+1]==' '))
+        {   opttext[i]='\n'; opttext[i+1]='\n';
             bestyet2[0].popularity++;
         }
 
-        if ((all_text[i]==',') && (all_text[i+1]==' '))
-        {   all_text[i]='\n'; all_text[i+1]='\n';
+        if ((opttext[i]==',') && (opttext[i+1]==' '))
+        {   opttext[i]='\n'; opttext[i+1]='\n';
             bestyet2[1].popularity++;
         }
     }
 
-    MAX_GTABLE=subtract_pointers(all_text_top,all_text)+1;
+    MAX_GTABLE=opttextlen+1;
     grandtable=my_calloc(4*sizeof(int32), MAX_GTABLE/4, "grandtable");
 
-    for (i=0, t=0; all_text+i<all_text_top; i++)
-    {   test.text[0]=all_text[i];
-        test.text[1]=all_text[i+1];
-        test.text[2]=all_text[i+2];
+    for (i=0, tcount=0; i<opttextlen; i++)
+    {
+        tlb test;
+        test.text[0]=opttext[i];
+        test.text[1]=opttext[i+1];
+        test.text[2]=opttext[i+2];
         test.text[3]=0;
         if ((test.text[0]=='\n')||(test.text[1]=='\n')||(test.text[2]=='\n'))
             goto DontKeep;
-        for (j=0; j<no_occs; j++)
+        for (j=0; j<no_occs; j++) {
             if (strcmp(test.text,tlbtab[j].text)==0)
                 goto DontKeep;
+        }
         test.occurrences=0;
-        for (j=i+3; all_text+j<all_text_top; j++)
+        test.intab=0;
+        for (j=i+3; j<opttextlen; j++)
         {
 #ifdef MAC_FACE
             if (j%((**g_pm_hndl).linespercheck) == 0)
             {   ProcessEvents (&g_proc);
                 if (g_proc != true)
-                {   free_arrays();
-                    if (store_the_text)
-                        my_free(&all_text,"transcription text");
+                {   ao_free_arrays();
                     longjmp (g_fallback, 1);
                 }
             }
 #endif
-            if ((all_text[i]==all_text[j])
-                 && (all_text[i+1]==all_text[j+1])
-                 && (all_text[i+2]==all_text[j+2]))
-                 {   grandtable[t+test.occurrences]=j;
+            if ((opttext[i]==opttext[j])
+                 && (opttext[i+1]==opttext[j+1])
+                 && (opttext[i+2]==opttext[j+2]))
+                 {   grandtable[tcount+test.occurrences]=j;
                      test.occurrences++;
-                     if (t+test.occurrences==MAX_GTABLE)
+                     if (tcount+test.occurrences==MAX_GTABLE)
                      {   printf("All %ld cross-references used\n",
                              (long int) MAX_GTABLE);
                          goto Built;
@@ -1387,16 +1630,14 @@ extern void optimise_abbreviations(void)
                  }
         }
         if (test.occurrences>=2)
-        {   tlbtab[no_occs]=test;
-            tlbtab[no_occs].intab=t; t+=tlbtab[no_occs].occurrences;
+        {
+            ensure_memory_list_available(&tlbtab_memlist, no_occs+1);
+            tlbtab[no_occs]=test;
+            tlbtab[no_occs].intab=tcount;
+            tcount += tlbtab[no_occs].occurrences;
             if (max<tlbtab[no_occs].occurrences)
                 max=tlbtab[no_occs].occurrences;
             no_occs++;
-            if (no_occs==MAX_TLBS)
-            {   printf("All %d three-letter-blocks used\n",
-                    MAX_TLBS);
-                goto Built;
-            }
         }
         DontKeep: ;
     }
@@ -1405,32 +1646,38 @@ extern void optimise_abbreviations(void)
     grandflags=my_calloc(sizeof(int), max, "grandflags");
 
 
-    printf("Cross-reference table (%ld entries) built...\n",
-        (long int) no_occs);
+    if (optabbrevs_trace_setting >= 1) {
+        printf("Cross-reference table (%ld entries) built...\n",
+            (long int) no_occs);
+    }
     /*  for (i=0; i<no_occs; i++)
             printf("%4d %4d '%s' %d\n",i,tlbtab[i].intab,tlbtab[i].text,
                 tlbtab[i].occurrences);
     */
 
-    for (i=0; i<64; i++) bestyet2[i].length=0; selected=2;
-    available=256;
-    while ((available>0)&&(selected<64))
-    {   printf("Pass %d\n", ++pass_no);
-
+    for (i=0; i<MAX_ABBREVS; i++) bestyet2[i].length=0;
+    available=MAX_BESTYET;
+    while ((available>0)&&(selected<MAX_ABBREVS))
+    {
+        pass_no++;
+        if (optabbrevs_trace_setting >= 1) {
+            printf("Pass %d\n", pass_no);
+        }
+        
         optimise_pass();
         available=0;
-        for (i=0; i<256; i++)
+        for (i=0; i<MAX_BESTYET; i++)
             if (bestyet[i].score!=0)
             {   available++;
                 nl=bestyet[i].length;
                 for (j2=0; j2<nl; j2++) bestyet[i].text[j2]=
-                    all_text[bestyet[i].location+j2];
+                    opttext[bestyet[i].location+j2];
                 bestyet[i].text[nl]=0;
             }
 
     /*  printf("End of pass results:\n");
         printf("\nno   score  freq   string\n");
-        for (i=0; i<256; i++)
+        for (i=0; i<MAX_BESTYET; i++)
             if (bestyet[i].score>0)
                 printf("%02d:  %4d   %4d   '%s'\n", i, bestyet[i].score,
                     bestyet[i].popularity, bestyet[i].text);
@@ -1438,40 +1685,44 @@ extern void optimise_abbreviations(void)
 
         do
         {   max=0;
-            for (i=0; i<256; i++)
+            for (i=0; i<MAX_BESTYET; i++)
                 if (max<bestyet[i].score)
                 {   max=bestyet[i].score;
                     maxat=i;
                 }
 
             if (max>0)
-            {   bestyet2[selected++]=bestyet[maxat];
-
-                printf(
-                    "Selection %2ld: '%s' (repeated %ld times, scoring %ld)\n",
-                    (long int) selected,bestyet[maxat].text,
-                    (long int) bestyet[maxat].popularity,
-                    (long int) bestyet[maxat].score);
+            {
+                char testtext[4];
+                bestyet2[selected++]=bestyet[maxat];
+
+                if (optabbrevs_trace_setting >= 1) {
+                    printf(
+                        "Selection %2ld: '%s' (repeated %ld times, scoring %ld)\n",
+                        (long int) selected,bestyet[maxat].text,
+                        (long int) bestyet[maxat].popularity,
+                        (long int) bestyet[maxat].score);
+                }
 
-                test.text[0]=bestyet[maxat].text[0];
-                test.text[1]=bestyet[maxat].text[1];
-                test.text[2]=bestyet[maxat].text[2];
-                test.text[3]=0;
+                testtext[0]=bestyet[maxat].text[0];
+                testtext[1]=bestyet[maxat].text[1];
+                testtext[2]=bestyet[maxat].text[2];
+                testtext[3]=0;
 
                 for (i=0; i<no_occs; i++)
-                    if (strcmp(test.text,tlbtab[i].text)==0)
+                    if (strcmp(testtext,tlbtab[i].text)==0)
                         break;
 
                 for (j=0; j<tlbtab[i].occurrences; j++)
                 {   if (memcmp(bestyet[maxat].text,
-                               all_text+grandtable[tlbtab[i].intab+j],
+                               opttext+grandtable[tlbtab[i].intab+j],
                                bestyet[maxat].length)==0)
                     {   for (j2=0; j2<bestyet[maxat].length; j2++)
-                            all_text[grandtable[tlbtab[i].intab+j]+j2]='\n';
+                            opttext[grandtable[tlbtab[i].intab+j]+j2]='\n';
                     }
                 }
 
-                for (i=0; i<256; i++)
+                for (i=0; i<MAX_BESTYET; i++)
                     if ((bestyet[i].score>0)&&
                         (any_overlap(bestyet[maxat].text,bestyet[i].text)==1))
                     {   bestyet[i].score=0;
@@ -1479,7 +1730,7 @@ extern void optimise_abbreviations(void)
                             bestyet[i].text); */
                     }
             }
-        } while ((max>0)&&(available>0)&&(selected<64));
+        } while ((max>0)&&(available>0)&&(selected<MAX_ABBREVS));
     }
 
     printf("\nChosen abbreviations (in Inform syntax):\n\n");
@@ -1507,11 +1758,11 @@ extern void optimise_abbreviations(void)
 /*        <Z-coded text>    <flags>   <verbnumber>     <adjectivenumber>     */
 /*        4 or 6 bytes       byte        byte             byte               */
 /*                                                                           */
-/*   For Glulx, the form is instead: (But see below about Unicode-valued     */
-/*   dictionaries and my heinie.)                                            */
+/*   For Glulx, the form is instead: (See below about Unicode-valued         */
+/*   dictionaries and DICT_WORD_BYTES.)                                      */
 /*                                                                           */
 /*        <tag>  <plain text>    <flags>  <verbnumber>   <adjectivenumber>   */
-/*         $60    DICT_WORD_SIZE  short    short          short              */
+/*         $60    DICT_WORD_BYTES short    short          short              */
 /*                                                                           */
 /*   These records are stored in "accession order" (i.e. in order of their   */
 /*   first being received by these routines) and only alphabetically sorted  */
@@ -1538,28 +1789,31 @@ extern void optimise_abbreviations(void)
 /*   fields. (The high bytes are $DICT_WORD_SIZE+1/3/5.)                     */
 /* ------------------------------------------------------------------------- */
 
-uchar *dictionary,                    /* (These two pointers are externally
+uchar *dictionary;                    /* (These two variables are externally
                                          used only in "tables.c" when
                                          building the story-file)            */
-    *dictionary_top;                  /* Pointer to next free record         */
+static memory_list dictionary_memlist;
+int32 dictionary_top;                 /* Position of the next free record
+                                         in dictionary (i.e., the current
+                                         number of bytes)                    */
 
 int dict_entries;                     /* Total number of records entered     */
 
 /* ------------------------------------------------------------------------- */
-/*   dict_word is a typedef for a struct of 6 unsigned chars (defined in     */
-/*   "header.h"): it holds the (4 or) 6 bytes of Z-coded text of a word.     */
+/*   dict_word was originally a typedef for a struct of 6 unsigned chars.    */
+/*   It held the (4 or) 6 bytes of Z-coded text of a word.                   */
 /*   Usefully, because the PAD character 5 is < all alphabetic characters,   */
 /*   alphabetic order corresponds to numeric order.  For this reason, the    */
 /*   dict_word is called the "sort code" of the original text word.          */
 /*                                                                           */
-/*   ###- In modifying the compiler, I've found it easier to discard the     */
+/*   In modifying the compiler for Glulx, I found it easier to discard the   */
 /*   typedef, and operate directly on uchar arrays of length DICT_WORD_SIZE. */
 /*   In Z-code, DICT_WORD_SIZE will be 6, so the Z-code compiler will work   */
 /*   as before. In Glulx, it can be any value up to MAX_DICT_WORD_SIZE.      */
 /*   (That limit is defined as 40 in the header; it exists only for a few    */
 /*   static buffers, and can be increased without using significant memory.) */
 /*                                                                           */
-/*   ###- Well, that certainly bit me on the butt, didn't it. In further     */
+/*   ...Well, that certainly bit me on the butt, didn't it. In further       */
 /*   modifying the compiler to generate a Unicode dictionary, I have to      */
 /*   store four-byte values in the uchar array. This is handled by making    */
 /*   the array size DICT_WORD_BYTES (which is DICT_WORD_SIZE*DICT_CHAR_SIZE).*/
@@ -1806,10 +2060,13 @@ typedef struct dict_tree_node_s
     char colour;                  /* The colour of the branch to the parent */
 } dict_tree_node;
 
-static dict_tree_node *dtree;
+static dict_tree_node *dtree;     /* Allocated to dict_entries */
+static memory_list dtree_memlist;
+
+static uchar *dict_sort_codes;  /* Allocated to dict_entries*DICT_WORD_BYTES */
+static memory_list dict_sort_codes_memlist;
 
-int   *final_dict_order;
-static uchar *dict_sort_codes;
+int   *final_dict_order;          /* Allocated at sort_dictionary() time */
 
 static void dictionary_begin_pass(void)
 {
@@ -1817,10 +2074,12 @@ static void dictionary_begin_pass(void)
     /*  Glulx has a 4-byte header instead. */
 
     if (!glulx_mode)
-        dictionary_top=dictionary+7;
+        dictionary_top = 7;
     else
-        dictionary_top=dictionary+4;
+        dictionary_top = 4;
 
+    ensure_memory_list_available(&dictionary_memlist, dictionary_top);
+    
     root = VACANT;
     dict_entries = 0;
 }
@@ -1836,6 +2095,9 @@ static void recursively_sort(int node)
 
 extern void sort_dictionary(void)
 {   int i;
+    
+    final_dict_order = my_calloc(sizeof(int), dict_entries, "final dictionary ordering table");
+    
     if (module_switch)
     {   for (i=0; i<dict_entries; i++)
             final_dict_order[i] = i;
@@ -1892,8 +2154,10 @@ extern int dictionary_add(char *dword, int x, int y, int z)
         if (n==0)
         {
             if (!glulx_mode) {
-                p = dictionary+7 + at*(3+res) + res;
-                p[0]=(p[0])|x; p[1]=(p[1])|y; p[2]=(p[2])|z;
+                p = dictionary+7 + at*DICT_ENTRY_BYTE_LENGTH + res;
+                p[0]=(p[0])|x; p[1]=(p[1])|y;
+                if (!ZCODE_LESS_DICT_DATA)
+                    p[2]=(p[2])|z;
                 if (x & 128) p[0] = (p[0])|number_and_case;
             }
             else {
@@ -1988,8 +2252,8 @@ extern int dictionary_add(char *dword, int x, int y, int z)
 
     CreateEntry:
 
-    if (dict_entries==MAX_DICT_ENTRIES)
-        memoryerror("MAX_DICT_ENTRIES",MAX_DICT_ENTRIES);
+    ensure_memory_list_available(&dtree_memlist, dict_entries+1);
+    ensure_memory_list_available(&dict_sort_codes_memlist, (dict_entries+1)*DICT_WORD_BYTES);
 
     dtree[dict_entries].branch[0] = VACANT;
     dtree[dict_entries].branch[1] = VACANT;
@@ -1999,7 +2263,8 @@ extern int dictionary_add(char *dword, int x, int y, int z)
 
     if (!glulx_mode) {
 
-        p = dictionary + (3+res)*dict_entries + 7;
+        ensure_memory_list_available(&dictionary_memlist, dictionary_top + DICT_ENTRY_BYTE_LENGTH);
+        p = dictionary + DICT_ENTRY_BYTE_LENGTH*dict_entries + 7;
 
         /*  So copy in the 4 (or 6) bytes of Z-coded text and the 3 data 
             bytes */
@@ -2008,14 +2273,16 @@ extern int dictionary_add(char *dword, int x, int y, int z)
         p[2]=prepared_sort[2]; p[3]=prepared_sort[3];
         if (version_number > 3)
           {   p[4]=prepared_sort[4]; p[5]=prepared_sort[5]; }
-        p[res]=x; p[res+1]=y; p[res+2]=z;
+        p[res]=x; p[res+1]=y;
+        if (!ZCODE_LESS_DICT_DATA) p[res+2]=z;
         if (x & 128) p[res] = (p[res])|number_and_case;
 
-        dictionary_top += res+3;
+        dictionary_top += DICT_ENTRY_BYTE_LENGTH;
 
     }
     else {
         int i;
+        ensure_memory_list_available(&dictionary_memlist, dictionary_top + DICT_ENTRY_BYTE_LENGTH);
         p = dictionary + 4 + DICT_ENTRY_BYTE_LENGTH*dict_entries;
         p[0] = 0x60; /* type byte -- dict word */
 
@@ -2052,7 +2319,7 @@ extern void dictionary_set_verb_number(char *dword, int to)
     if (i!=0)
     {   
         if (!glulx_mode) {
-            p=dictionary+7+(i-1)*(3+res)+res; 
+            p=dictionary+7+(i-1)*DICT_ENTRY_BYTE_LENGTH+res; 
             p[1]=to;
         }
         else {
@@ -2156,6 +2423,10 @@ extern void word_to_ascii(uchar *p, char *results)
         encoded_word[7] = 8*(((int) p[4])&0x3) + (((int) p[5])&0xe0)/32;
         encoded_word[8] = ((int) p[5])&0x1f;
     }
+    else
+    {
+        encoded_word[6] = encoded_word[7] = encoded_word[8] = 0;
+    }
 
     shift = 0; cc = 0;
     for (i=0; i< ((version_number==3)?6:9); i++)
@@ -2185,15 +2456,49 @@ extern void word_to_ascii(uchar *p, char *results)
     results[cc] = 0;
 }
 
-static void recursively_show_z(int node)
+/* Print a dictionary word to stdout. 
+   (This assumes that d_show_buf is null.)
+ */
+void print_dict_word(int node)
+{
+    uchar *p;
+    int cprinted;
+    
+    if (!glulx_mode) {
+        char textual_form[32];
+        p = (uchar *)dictionary + 7 + DICT_ENTRY_BYTE_LENGTH*node;
+        
+        word_to_ascii(p, textual_form);
+        
+        for (cprinted = 0; textual_form[cprinted]!=0; cprinted++)
+            show_char(textual_form[cprinted]);
+    }
+    else {
+        p = (uchar *)dictionary + 4 + DICT_ENTRY_BYTE_LENGTH*node;
+        
+        for (cprinted = 0; cprinted<DICT_WORD_SIZE; cprinted++)
+        {
+            uint32 ch;
+            if (DICT_CHAR_SIZE == 1)
+                ch = p[1+cprinted];
+            else
+                ch = (p[4*cprinted+4] << 24) + (p[4*cprinted+5] << 16) + (p[4*cprinted+6] << 8) + (p[4*cprinted+7]);
+            if (!ch)
+                break;
+            show_uchar(ch);
+        }
+    }
+}
+
+static void recursively_show_z(int node, int level)
 {   int i, cprinted, flags; uchar *p;
     char textual_form[32];
     int res = (version_number == 3)?4:6; /* byte length of encoded text */
 
     if (dtree[node].branch[0] != VACANT)
-        recursively_show_z(dtree[node].branch[0]);
+        recursively_show_z(dtree[node].branch[0], level);
 
-    p = (uchar *)dictionary + 7 + (3+res)*node;
+    p = (uchar *)dictionary + 7 + DICT_ENTRY_BYTE_LENGTH*node;
 
     word_to_ascii(p, textual_form);
 
@@ -2202,8 +2507,12 @@ static void recursively_show_z(int node)
     for (; cprinted < 4 + ((version_number==3)?6:9); cprinted++)
         show_char(' ');
 
-    if (d_show_buf == NULL)
-    {   for (i=0; i<3+res; i++) printf("%02x ",p[i]);
+    /* The level-1 info can only be printfed (d_show_buf must be null). */
+    if (d_show_buf == NULL && level >= 1)
+    {
+        if (level >= 2) {
+            for (i=0; i<DICT_ENTRY_BYTE_LENGTH; i++) printf("%02x ",p[i]);
+        }
 
         flags = (int) p[res];
         if (flags & 128)
@@ -2231,15 +2540,15 @@ static void recursively_show_z(int node)
     }
 
     if (dtree[node].branch[1] != VACANT)
-        recursively_show_z(dtree[node].branch[1]);
+        recursively_show_z(dtree[node].branch[1], level);
 }
 
-static void recursively_show_g(int node)
+static void recursively_show_g(int node, int level)
 {   int i, cprinted;
     uchar *p;
 
     if (dtree[node].branch[0] != VACANT)
-        recursively_show_g(dtree[node].branch[0]);
+        recursively_show_g(dtree[node].branch[0], level);
 
     p = (uchar *)dictionary + 4 + DICT_ENTRY_BYTE_LENGTH*node;
 
@@ -2257,11 +2566,14 @@ static void recursively_show_g(int node)
     for (; cprinted<DICT_WORD_SIZE+4; cprinted++)
         show_char(' ');
 
-    if (d_show_buf == NULL)
-    {   for (i=0; i<DICT_ENTRY_BYTE_LENGTH; i++) printf("%02x ",p[i]);
-        int flagpos = (DICT_CHAR_SIZE == 1) ? (DICT_WORD_SIZE+1) : (DICT_WORD_BYTES+4);
+    /* The level-1 info can only be printfed (d_show_buf must be null). */
+    if (d_show_buf == NULL && level >= 1)
+    {   int flagpos = (DICT_CHAR_SIZE == 1) ? (DICT_WORD_SIZE+1) : (DICT_WORD_BYTES+4);
         int flags = (p[flagpos+0] << 8) | (p[flagpos+1]);
         int verbnum = (p[flagpos+2] << 8) | (p[flagpos+3]);
+        if (level >= 2) {
+            for (i=0; i<DICT_ENTRY_BYTE_LENGTH; i++) printf("%02x ",p[i]);
+        }
         if (flags & 128)
         {   printf("noun ");
             if (flags & 4)  printf("p"); else printf(" ");
@@ -2284,7 +2596,7 @@ static void recursively_show_g(int node)
     }
 
     if (dtree[node].branch[1] != VACANT)
-        recursively_show_g(dtree[node].branch[1]);
+        recursively_show_g(dtree[node].branch[1], level);
 }
 
 static void show_alphabet(int i)
@@ -2303,14 +2615,17 @@ static void show_alphabet(int i)
     printf("\n");
 }
 
-extern void show_dictionary(void)
-{   printf("Dictionary contains %d entries:\n",dict_entries);
+extern void show_dictionary(int level)
+{
+    /* Level 0: show words only. Level 1: show words and flags.
+       Level 2: also show bytes.*/
+    printf("Dictionary contains %d entries:\n",dict_entries);
     if (dict_entries != 0)
     {   d_show_len = 0; d_show_buf = NULL; 
         if (!glulx_mode)    
-            recursively_show_z(root);
+            recursively_show_z(root, level);
         else
-            recursively_show_g(root);
+            recursively_show_g(root, level);
     }
     if (!glulx_mode)
     {
@@ -2335,9 +2650,9 @@ extern void write_dictionary_to_transcript(void)
     if (dict_entries != 0)
     {
         if (!glulx_mode)    
-            recursively_show_z(root);
+            recursively_show_z(root, 0);
         else
-            recursively_show_g(root);
+            recursively_show_g(root, 0);
     }
     if (d_show_len != 0) write_to_transcript_file(d_show_buf, STRCTX_DICT);
 
@@ -2351,32 +2666,45 @@ extern void write_dictionary_to_transcript(void)
 
 extern void init_text_vars(void)
 {   int j;
+
+    opttext = NULL;
+    opttextlen = 0;
     bestyet = NULL;
     bestyet2 = NULL;
     tlbtab = NULL;
     grandtable = NULL;
     grandflags = NULL;
-    no_chars_transcribed = 0;
+
+    all_text = NULL;
 
     for (j=0; j<256; j++) abbrevs_lookup[j] = -1;
 
     total_zchars_trans = 0;
 
+    dictionary = NULL;
+    dictionary_top = 0;
     dtree = NULL;
     final_dict_order = NULL;
     dict_sort_codes = NULL;
     dict_entries=0;
 
-    initialise_memory_block(&static_strings_area);
+    static_strings_area = NULL;
+    abbreviations_optimal_parse_schedule = NULL;
+    abbreviations_optimal_parse_scores = NULL;
+
+    compressed_offsets = NULL;
+    huff_entities = NULL;
+    hufflist = NULL;
+    unicode_usage_entries = NULL;
 }
 
 extern void text_begin_pass(void)
 {   abbrevs_lookup_table_made = FALSE;
     no_abbreviations=0;
     total_chars_trans=0; total_bytes_trans=0;
-    if (store_the_text) all_text_top=all_text;
+    all_text_top=0;
     dictionary_begin_pass();
-    low_strings_top = low_strings;
+    low_strings_top = 0;
 
     static_strings_extent = 0;
     no_strings = 0;
@@ -2387,29 +2715,55 @@ extern void text_begin_pass(void)
 /*  Note: for allocation and deallocation of all_the_text, see inform.c      */
 
 extern void text_allocate_arrays(void)
-{   abbreviations_at = my_malloc(MAX_ABBREVS*MAX_ABBREV_LENGTH,
-        "abbreviations");
-    abbrev_values    = my_calloc(sizeof(int), MAX_ABBREVS, "abbrev values");
-    abbrev_quality   = my_calloc(sizeof(int), MAX_ABBREVS, "abbrev quality");
-    abbrev_freqs     = my_calloc(sizeof(int),   MAX_ABBREVS, "abbrev freqs");
+{
+    int ix;
 
-    dtree            = my_calloc(sizeof(dict_tree_node), MAX_DICT_ENTRIES,
-                                 "red-black tree for dictionary");
-    final_dict_order = my_calloc(sizeof(int),  MAX_DICT_ENTRIES,
-                                 "final dictionary ordering table");
-    dict_sort_codes  = my_calloc(DICT_WORD_BYTES, MAX_DICT_ENTRIES,
-                                 "dictionary sort codes");
+    initialise_memory_list(&translated_text_memlist,
+        sizeof(uchar), 8000, (void**)&translated_text,
+        "translated text holding area");
+    
+    initialise_memory_list(&all_text_memlist,
+        sizeof(char), 0, (void**)&all_text,
+        "transcription text for optimise");
+    
+    initialise_memory_list(&static_strings_area_memlist,
+        sizeof(uchar), 128, (void**)&static_strings_area,
+        "static strings area");
+    
+    initialise_memory_list(&abbreviations_at_memlist,
+        MAX_ABBREV_LENGTH, 64, (void**)&abbreviations_at,
+        "abbreviation text");
 
-    if (!glulx_mode)
-        dictionary = my_malloc(9*MAX_DICT_ENTRIES+7,
-            "dictionary");
-    else
-        dictionary = my_malloc(DICT_ENTRY_BYTE_LENGTH*MAX_DICT_ENTRIES+4,
-            "dictionary");
+    initialise_memory_list(&abbreviations_memlist,
+        sizeof(abbreviation), 64, (void**)&abbreviations,
+        "abbreviations");
 
-    strings_holding_area
-         = my_malloc(MAX_STATIC_STRINGS,"static strings holding area");
-    low_strings = my_malloc(MAX_LOW_STRINGS,"low (abbreviation) strings");
+    initialise_memory_list(&abbreviations_optimal_parse_schedule_memlist,
+        sizeof(int), 0, (void**)&abbreviations_optimal_parse_schedule,
+        "abbreviations optimal parse schedule");
+    initialise_memory_list(&abbreviations_optimal_parse_scores_memlist,
+        sizeof(int), 0, (void**)&abbreviations_optimal_parse_scores,
+        "abbreviations optimal parse scores");
+    
+    initialise_memory_list(&dtree_memlist,
+        sizeof(dict_tree_node), 1500, (void**)&dtree,
+        "red-black tree for dictionary");
+    initialise_memory_list(&dict_sort_codes_memlist,
+        sizeof(uchar), 1500*DICT_WORD_BYTES, (void**)&dict_sort_codes,
+        "dictionary sort codes");
+
+    final_dict_order = NULL; /* will be allocated at sort_dictionary() time */
+
+    /* The exact size will be 7+7*num for z3, 7+9*num for z4+, 
+       4+DICT_ENTRY_BYTE_LENGTH*num for Glulx. But this is just an initial
+       allocation; we don't have to be precise. */
+    initialise_memory_list(&dictionary_memlist,
+        sizeof(uchar), 1000*DICT_ENTRY_BYTE_LENGTH, (void**)&dictionary,
+        "dictionary");
+
+    initialise_memory_list(&low_strings_memlist,
+        sizeof(uchar), 1024, (void**)&low_strings,
+        "low (abbreviation) strings");
 
     d_show_buf = NULL;
     d_show_size = 0;
@@ -2422,57 +2776,78 @@ extern void text_allocate_arrays(void)
     compression_table_size = 0;
     compressed_offsets = NULL;
 
-    MAX_CHARACTER_SET = 0;
+    initialise_memory_list(&unicode_usage_entries_memlist,
+        sizeof(unicode_usage_t), 0, (void**)&unicode_usage_entries,
+        "unicode entity entries");
 
-    if (glulx_mode) {
-      if (compression_switch) {
-        int ix;
-        MAX_CHARACTER_SET = 257 + MAX_ABBREVS + MAX_DYNAMIC_STRINGS 
-          + MAX_UNICODE_CHARS;
-        huff_entities = my_calloc(sizeof(huffentity_t), MAX_CHARACTER_SET*2+1, 
-          "huffman entities");
-        hufflist = my_calloc(sizeof(huffentity_t *), MAX_CHARACTER_SET, 
-          "huffman node list");
-        unicode_usage_entries = my_calloc(sizeof(unicode_usage_t), 
-          MAX_UNICODE_CHARS, "unicode entity entries");
-        for (ix=0; ix<UNICODE_HASH_BUCKETS; ix++)
-          unicode_usage_hash[ix] = NULL;
-      }
-      compressed_offsets = my_calloc(sizeof(int32), MAX_NUM_STATIC_STRINGS,
+    /* hufflist and huff_entities will be allocated at compress_game_text() time. */
+
+    /* This hash table is only used in Glulx */
+    for (ix=0; ix<UNICODE_HASH_BUCKETS; ix++)
+        unicode_usage_hash[ix] = -1;
+    
+    initialise_memory_list(&compressed_offsets_memlist,
+        sizeof(int32), 0, (void**)&compressed_offsets,
         "static strings index table");
-    }
+}
+
+extern void extract_all_text()
+{
+    /* optimise_abbreviations() is called after free_arrays(). Therefore,
+       we need to preserve the text transcript where it will not be
+       freed up. We do this by copying the pointer to opttext. */
+    opttext = all_text;
+    opttextlen = all_text_top;
+
+    /* Re-init all_text_memlist. This causes it to forget all about the
+       old pointer. Deallocating it in text_free_arrays() will be a no-op. */
+    initialise_memory_list(&all_text_memlist,
+        sizeof(char), 0, (void**)&all_text,
+        "dummy transcription text");
 }
 
 extern void text_free_arrays(void)
 {
-    my_free(&strings_holding_area, "static strings holding area");
-    my_free(&low_strings, "low (abbreviation) strings");
-    my_free(&abbreviations_at, "abbreviations");
-    my_free(&abbrev_values,    "abbrev values");
-    my_free(&abbrev_quality,   "abbrev quality");
-    my_free(&abbrev_freqs,     "abbrev freqs");
-
-    my_free(&dtree,            "red-black tree for dictionary");
+    deallocate_memory_list(&translated_text_memlist);
+    
+    deallocate_memory_list(&all_text_memlist);
+    
+    deallocate_memory_list(&low_strings_memlist);
+    deallocate_memory_list(&abbreviations_at_memlist);
+    deallocate_memory_list(&abbreviations_memlist);
+
+    deallocate_memory_list(&abbreviations_optimal_parse_schedule_memlist);
+    deallocate_memory_list(&abbreviations_optimal_parse_scores_memlist);
+
+    deallocate_memory_list(&dtree_memlist);
+    deallocate_memory_list(&dict_sort_codes_memlist);
     my_free(&final_dict_order, "final dictionary ordering table");
-    my_free(&dict_sort_codes,  "dictionary sort codes");
 
-    my_free(&dictionary,"dictionary");
+    deallocate_memory_list(&dictionary_memlist);
 
-    my_free(&compressed_offsets, "static strings index table");
+    deallocate_memory_list(&compressed_offsets_memlist);
     my_free(&hufflist, "huffman node list");
     my_free(&huff_entities, "huffman entities");
-    my_free(&unicode_usage_entries, "unicode entity entities");
+    
+    deallocate_memory_list(&unicode_usage_entries_memlist);
 
-    deallocate_memory_block(&static_strings_area);
+    deallocate_memory_list(&static_strings_area_memlist);
 }
 
 extern void ao_free_arrays(void)
-{   my_free (&tlbtab,"tlb table");
-    my_free (&sub_buffer,"sub_buffer");
+{
+    /* Called only after optimise_abbreviations() runs. */
+    
+    my_free (&opttext,"stashed transcript for optimisation");
     my_free (&bestyet,"bestyet");
     my_free (&bestyet2,"bestyet2");
     my_free (&grandtable,"grandtable");
     my_free (&grandflags,"grandflags");
+
+    deallocate_memory_list(&tlbtab_memlist);
+    
+    /* This was re-inited, so we should re-deallocate it. */
+    deallocate_memory_list(&all_text_memlist);
 }
 
 /* ========================================================================= */
index d32ad40c125db1e0460160e8e22cce81e24f235b..ce29ceb566e464cc261c5460417fd202540693c5 100644 (file)
@@ -3,8 +3,8 @@
 /*              by the compiler (e.g. DefArt) which the program doesn't      */
 /*              provide                                                      */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -17,7 +17,7 @@
 /* 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/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -27,7 +27,7 @@ int veneer_mode;                      /*  Is the code currently being
                                           compiled from the veneer?          */
 
 static debug_locations null_debug_locations =
-    { { 0, 0, 0, 0, 0, 0, 0 }, NULL, 0 };
+    { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, 0 };
 
 extern void compile_initial_routine(void)
 {
@@ -36,6 +36,10 @@ extern void compile_initial_routine(void)
         (since the interpreter begins execution with an empty stack frame):
         and it must "quit" rather than "return".
 
+        (Pedantically, in Z-code 1-5, this is not a routine at all. It's
+        a sequence of opcodes which ends with "quit". The one-byte
+        header generated by assemble_routine_header() is a dummy.)
+
         In order not to impose these restrictions on "Main", we compile a
         trivial routine consisting of a call to "Main" followed by "quit".   */
 
@@ -46,8 +50,8 @@ extern void compile_initial_routine(void)
     assign_symbol(j,
         assemble_routine_header(0, FALSE, "Main__", FALSE, j),
         ROUTINE_T);
-    sflags[j] |= SYSTEM_SFLAG + USED_SFLAG;
-    if (trace_fns_setting==3) sflags[j] |= STAR_SFLAG;
+    symbols[j].flags |= SYSTEM_SFLAG + USED_SFLAG;
+    if (trace_fns_setting==3) symbols[j].flags |= STAR_SFLAG;
 
     if (!glulx_mode) {
 
@@ -2182,28 +2186,42 @@ extern assembly_operand veneer_routine(int code)
     return(AO);
 }
 
+extern char *veneer_routine_name(int code)
+{
+    if (code < 0 || code >= VENEER_ROUTINES) {
+        return "???";
+    }
+    if (!glulx_mode) {
+        return VRs_z[code].name;
+    }
+    else {
+        return VRs_g[code].name;
+    }
+}
+
 static void compile_symbol_table_routine(void)
 {   int32 j, nl, arrays_l, routines_l, constants_l;
     assembly_operand AO, AO2, AO3;
 
     /* Assign local var names for the benefit of the debugging information 
-       file. */
-    local_variable_texts[0] = "dummy1";
-    local_variable_texts[1] = "dummy2";
+       file. (We don't set local_variable.keywords because we're not
+       going to be parsing any code.) */
+    strcpy(local_variable_names[0].text, "dummy1");
+    strcpy(local_variable_names[1].text, "dummy2");
 
     veneer_mode = TRUE; j = symbol_index("Symb__Tab", -1);
     assign_symbol(j,
         assemble_routine_header(2, FALSE, "Symb__Tab", FALSE, j),
         ROUTINE_T);
-    sflags[j] |= SYSTEM_SFLAG + USED_SFLAG;
-    if (trace_fns_setting==3) sflags[j] |= STAR_SFLAG;
+    symbols[j].flags |= SYSTEM_SFLAG + USED_SFLAG;
+    if (trace_fns_setting==3) symbols[j].flags |= STAR_SFLAG;
 
   if (!glulx_mode) {
 
     if (define_INFIX_switch == FALSE)
     {   assemblez_0(rfalse_zc);
-        variable_usage[1] = TRUE;
-        variable_usage[2] = TRUE;
+        variables[1].usage = TRUE;
+        variables[2].usage = TRUE;
         assemble_routine_end(FALSE, null_debug_locations);
         veneer_mode = FALSE;
         return;
@@ -2238,16 +2256,16 @@ static void compile_symbol_table_routine(void)
             nl = next_label++;
             sequence_point_follows = FALSE;
             assemblez_2_branch(je_zc, AO, AO2, nl, FALSE);
-            AO3.value = array_sizes[j];
+            AO3.value = arrays[j].size;
             AO3.marker = 0;
             assemblez_store(temp_var2, AO3);
-            AO3.value = array_types[j];
-            if (sflags[array_symbols[j]] & (INSF_SFLAG+SYSTEM_SFLAG))
+            AO3.value = arrays[j].type;
+            if (symbols[arrays[j].symbol].flags & (INSF_SFLAG+SYSTEM_SFLAG))
                 AO3.value = AO3.value + 16;
             AO3.marker = 0;
             assemblez_store(temp_var3, AO3);
-            AO3.value = svals[array_symbols[j]];
-            AO3.marker = (!array_locs[j] ? ARRAY_MV : STATIC_ARRAY_MV);
+            AO3.value = symbols[arrays[j].symbol].value;
+            AO3.marker = (!arrays[j].loc ? ARRAY_MV : STATIC_ARRAY_MV);
             assemblez_1(ret_zc, AO3);
             assemble_label_no(nl);
         }
@@ -2263,11 +2281,11 @@ static void compile_symbol_table_routine(void)
         sequence_point_follows = FALSE;
         assemblez_2_branch(je_zc, AO, AO2, nl, FALSE);
         AO3.value = 0;
-        if (sflags[named_routine_symbols[j]]
+        if (symbols[named_routine_symbols[j]].flags
             & (INSF_SFLAG+SYSTEM_SFLAG)) AO3.value = 16;
         AO3.marker = 0;
         assemblez_store(temp_var3, AO3);
-        AO3.value = svals[named_routine_symbols[j]];
+        AO3.value = symbols[named_routine_symbols[j]].value;
         AO3.marker = IROUTINE_MV;
         assemblez_1(ret_zc, AO3);
         assemble_label_no(nl);
@@ -2277,9 +2295,9 @@ static void compile_symbol_table_routine(void)
 
     assemble_label_no(constants_l);
     for (j=0, no_named_constants=0; j<no_symbols; j++)
-    {   if (((stypes[j] == OBJECT_T) || (stypes[j] == CLASS_T)
-            || (stypes[j] == CONSTANT_T))
-            && ((sflags[j] & (UNKNOWN_SFLAG+ACTION_SFLAG))==0))
+    {   if (((symbols[j].type == OBJECT_T) || (symbols[j].type == CLASS_T)
+            || (symbols[j].type == CONSTANT_T))
+            && ((symbols[j].flags & (UNKNOWN_SFLAG+ACTION_SFLAG))==0))
         {   AO2.value = no_named_constants++;
             if (AO2.value<256) AO2.type = SHORT_CONSTANT_OT;
             else AO2.type = LONG_CONSTANT_OT;
@@ -2287,9 +2305,9 @@ static void compile_symbol_table_routine(void)
             sequence_point_follows = FALSE;
             assemblez_2_branch(je_zc, AO, AO2, nl, FALSE);
             AO3.value = 0;
-            if (stypes[j] == OBJECT_T) AO3.value = 2;
-            if (stypes[j] == CLASS_T) AO3.value = 1;
-            if (sflags[j] & (INSF_SFLAG+SYSTEM_SFLAG))
+            if (symbols[j].type == OBJECT_T) AO3.value = 2;
+            if (symbols[j].type == CLASS_T) AO3.value = 1;
+            if (symbols[j].flags & (INSF_SFLAG+SYSTEM_SFLAG))
                 AO3.value = AO3.value + 16;
             AO3.marker = 0;
             assemblez_store(temp_var3, AO3);
@@ -2303,8 +2321,8 @@ static void compile_symbol_table_routine(void)
 
     sequence_point_follows = FALSE;
     assemblez_0(rfalse_zc);
-    variable_usage[1] = TRUE;
-    variable_usage[2] = TRUE;
+    variables[1].usage = TRUE;
+    variables[2].usage = TRUE;
     assemble_routine_end(FALSE, null_debug_locations);
     veneer_mode = FALSE;
   }
@@ -2312,8 +2330,8 @@ static void compile_symbol_table_routine(void)
 
     if (define_INFIX_switch == FALSE)
     {   assembleg_1(return_gc, zero_operand);
-        variable_usage[1] = TRUE;
-        variable_usage[2] = TRUE;
+        variables[1].usage = TRUE;
+        variables[2].usage = TRUE;
         assemble_routine_end(FALSE, null_debug_locations);
         veneer_mode = FALSE;
         return;
@@ -2348,7 +2366,7 @@ extern void compile_veneer(void)
         for (i=0; i<VENEER_ROUTINES; i++)
         {   if (veneer_routine_needs_compilation[i] == VR_CALLED)
             {   j = symbol_index(VRs[i].name, -1);
-                if (sflags[j] & UNKNOWN_SFLAG)
+                if (symbols[j].flags & UNKNOWN_SFLAG)
                 {   veneer_mode = TRUE;
                     strcpy(veneer_source_area, VRs[i].source1);
                     strcat(veneer_source_area, VRs[i].source2);
@@ -2361,18 +2379,18 @@ extern void compile_veneer(void)
                             VRs[i].name, TRUE, j),
                         ROUTINE_T);
                     veneer_mode = FALSE;
-                    if (trace_fns_setting==3) sflags[j] |= STAR_SFLAG;
+                    if (trace_fns_setting==3) symbols[j].flags |= STAR_SFLAG;
                 }
                 else
-                {   if (stypes[j] != ROUTINE_T)
+                {   if (symbols[j].type != ROUTINE_T)
                 error_named("The following name is reserved by Inform for its \
 own use as a routine name; you can use it as a routine name yourself (to \
 override the standard definition) but cannot use it for anything else:",
                         VRs[i].name);
                     else
-                        sflags[j] |= USED_SFLAG;
+                        symbols[j].flags |= USED_SFLAG;
                 }
-                veneer_routine_address[i] = svals[j];
+                veneer_routine_address[i] = symbols[j].value;
                 veneer_routine_needs_compilation[i] = VR_COMPILED;
                 try_veneer_again = TRUE;
             }
index 962b32f847a3e9a46962745549da8488547e0ece..31b42bb2ee999a5c92087c6c2c840f7e5ea9aa64 100644 (file)
@@ -2,8 +2,8 @@
 /*   "verbs" :  Manages actions and grammar tables; parses the directives    */
 /*              Verb and Extend.                                             */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -31,13 +31,8 @@ int32 grammar_version_symbol;          /* Index of "Grammar__Version"
 /* ------------------------------------------------------------------------- */
 /*   Array defined below:                                                    */
 /*                                                                           */
-/*    int32   action_byte_offset[n]       The (byte) offset in the Z-machine */
-/*                                        code area of the ...Sub routine    */
-/*                                        for action n.  (NB: This is left   */
-/*                                        blank until the end of the         */
-/*                                        compilation pass.)                 */
-/*    int32   action_symbol[n]            The symbol table index of the n-th */
-/*                                        action's name.                     */
+/*    actioninfo actions[n]               Symbol table index and byte offset */
+/*                                        of the ...Sub routine              */
 /* ------------------------------------------------------------------------- */
 
 int no_actions,                        /* Number of actions made so far      */
@@ -83,42 +78,228 @@ int no_Inform_verbs,                   /* Number of Inform-verbs made so far */
 /*   The format of this list is a sequence of variable-length records:       */
 /*                                                                           */
 /*     Byte offset to start of next record  (1 byte)                         */
-/*     Inform verb number this word corresponds to  (1 byte)                 */
+/*     Inform verb number this word corresponds to  (2 bytes)                */
 /*     The English verb-word (reduced to lower case), null-terminated        */
 /* ------------------------------------------------------------------------- */
 
-static char *English_verb_list,        /* First byte of first record         */
-            *English_verb_list_top;    /* Next byte free for new record      */
+static char *English_verb_list;       /* Allocated to English_verb_list_size */
+static memory_list English_verb_list_memlist;
 
-static int English_verb_list_size;     /* Size of the list in bytes
-                                          (redundant but convenient)         */
+static int English_verb_list_size;     /* Size of the list in bytes          */
 
-/* Maximum synonyms in a single Verb/Extend directive */
-#define MAX_VERB_SYNONYMS (32)
+static char *English_verbs_given;      /* Allocated to verbs_given_pos
+                                          (Used only within make_verb())     */
+static memory_list English_verbs_given_memlist;
 
 /* ------------------------------------------------------------------------- */
 /*   Arrays used by this file                                                */
 /* ------------------------------------------------------------------------- */
 
-  verbt   *Inform_verbs;
-  uchar   *grammar_lines;
+  verbt   *Inform_verbs;  /* Allocated up to no_Inform_verbs */
+  static memory_list Inform_verbs_memlist;
+  uchar   *grammar_lines; /* Allocated to grammar_lines_top */
+  static memory_list grammar_lines_memlist;
   int32    grammar_lines_top;
   int      no_grammar_lines, no_grammar_tokens;
 
-  int32   *action_byte_offset,
-          *action_symbol,
-          *grammar_token_routine,
-          *adjectives;
-  static uchar *adjective_sort_code;
+  actioninfo *actions; /* Allocated to no_actions */
+  memory_list actions_memlist;
+  int32   *grammar_token_routine; /* Allocated to no_grammar_token_routines */
+  static memory_list grammar_token_routine_memlist;
+
+  int32   *adjectives; /* Allocated to no_adjectives */
+  static memory_list adjectives_memlist;
+
+  static uchar *adjective_sort_code; /* Allocated to no_adjectives*DICT_WORD_BYTES */
+  static memory_list adjective_sort_code_memlist;
 
 /* ------------------------------------------------------------------------- */
 /*   Tracing for compiler maintenance                                        */
 /* ------------------------------------------------------------------------- */
 
+static char *find_verb_by_number(int num);
+
+static void list_grammar_line_v1(int mark)
+{
+    int action, actsym;
+    int ix, len;
+    char *str;
+
+    /* There is no GV1 for Glulx. */
+    if (glulx_mode)
+        return;
+    
+    action = (grammar_lines[mark] << 8) | (grammar_lines[mark+1]);
+    mark += 2;
+    
+    printf("  *");
+    while (grammar_lines[mark] != 15) {
+        uchar tok = grammar_lines[mark];
+        mark += 3;
+        
+        switch (tok) {
+        case 0:
+            printf(" noun");
+            break;
+        case 1:
+            printf(" held");
+            break;
+        case 2:
+            printf(" multi");
+            break;
+        case 3:
+            printf(" multiheld");
+            break;
+        case 4:
+            printf(" multiexcept");
+            break;
+        case 5:
+            printf(" multiinside");
+            break;
+        case 6:
+            printf(" creature");
+            break;
+        case 7:
+            printf(" special");
+            break;
+        case 8:
+            printf(" number");
+            break;
+        default:
+            if (tok >= 16 && tok < 48) {
+                printf(" noun=%d", tok-16);
+            }
+            else if (tok >= 48 && tok < 80) {
+                printf(" routine=%d", tok-48);
+            }
+            else if (tok >= 80 && tok < 128) {
+                printf(" scope=%d", tok-80);
+            }
+            else if (tok >= 128 && tok < 160) {
+                printf(" attr=%d", tok-128);
+            }
+            else if (tok >= 160) {
+                printf(" prep=%d", tok);
+            }
+            else {
+                printf(" ???");
+            }
+        }
+    }
+
+    printf(" -> ");
+    actsym = actions[action].symbol;
+    str = (symbols[actsym].name);
+    len = strlen(str) - 3;   /* remove "__A" */
+    for (ix=0; ix<len; ix++) putchar(str[ix]);
+    printf("\n");
+}
+
+static void list_grammar_line_v2(int mark)
+{
+    int action, flags, actsym;
+    int ix, len;
+    char *str;
+    
+    if (!glulx_mode) {
+        action = (grammar_lines[mark] << 8) | (grammar_lines[mark+1]);
+        flags = (action & 0x400);
+        action &= 0x3FF;
+        mark += 2;
+    }
+    else {
+        action = (grammar_lines[mark] << 8) | (grammar_lines[mark+1]);
+        mark += 2;
+        flags = grammar_lines[mark++];
+    }
+    
+    printf("  *");
+    while (grammar_lines[mark] != 15) {
+        int toktype, tokdat, tokalt;
+        if (!glulx_mode) {
+            toktype = grammar_lines[mark] & 0x0F;
+            tokalt = (grammar_lines[mark] >> 4) & 0x03;
+            mark += 1;
+            tokdat = (grammar_lines[mark] << 8) | (grammar_lines[mark+1]);
+            mark += 2;
+        }
+        else {
+            toktype = grammar_lines[mark] & 0x0F;
+            tokalt = (grammar_lines[mark] >> 4) & 0x03;
+            mark += 1;
+            tokdat = (grammar_lines[mark] << 24) | (grammar_lines[mark+1] << 16) | (grammar_lines[mark+2] << 8) | (grammar_lines[mark+3]);
+            mark += 4;
+        }
+
+        if (tokalt == 3 || tokalt == 1)
+            printf(" /");
+                
+        switch (toktype) {
+        case 1:
+            switch (tokdat) {
+            case 0: printf(" noun"); break;
+            case 1: printf(" held"); break;
+            case 2: printf(" multi"); break;
+            case 3: printf(" multiheld"); break;
+            case 4: printf(" multiexcept"); break;
+            case 5: printf(" multiinside"); break;
+            case 6: printf(" creature"); break;
+            case 7: printf(" special"); break;
+            case 8: printf(" number"); break;
+            case 9: printf(" topic"); break;
+            default: printf(" ???"); break;
+            }
+            break;
+        case 2:
+            printf(" '");
+            print_dict_word(tokdat);
+            printf("'");
+            break;
+        case 3:
+            printf(" noun=%d", tokdat);
+            break;
+        case 4:
+            printf(" attr=%d", tokdat);
+            break;
+        case 5:
+            printf(" scope=%d", tokdat);
+            break;
+        case 6:
+            printf(" routine=%d", tokdat);
+            break;
+        default:
+            printf(" ???%d:%d", toktype, tokdat);
+            break;
+        }
+    }
+    printf(" -> ");
+    actsym = actions[action].symbol;
+    str = (symbols[actsym].name);
+    len = strlen(str) - 3;   /* remove "__A" */
+    for (ix=0; ix<len; ix++) putchar(str[ix]);
+    if (flags) printf(" (reversed)");
+    printf("\n");
+}
+
 extern void list_verb_table(void)
-{   int i;
-    for (i=0; i<no_Inform_verbs; i++)
-        printf("Verb %2d has %d lines\n", i, Inform_verbs[i].lines);
+{
+    int verb, lx;
+    printf("Grammar table: %d verbs\n", no_Inform_verbs);
+    for (verb=0; verb<no_Inform_verbs; verb++) {
+        char *verbword = find_verb_by_number(verb);
+        printf("Verb '%s'\n", verbword);
+        for (lx=0; lx<Inform_verbs[verb].lines; lx++) {
+            int mark = Inform_verbs[verb].l[lx];
+            switch (grammar_version_number) {
+            case 1:
+                list_grammar_line_v1(mark);
+                break;
+            case 2:
+                list_grammar_line_v2(mark);
+                break;
+            }
+        }
+    }
 }
 
 /* ------------------------------------------------------------------------- */
@@ -131,7 +312,7 @@ static void new_action(char *b, int c)
         by using make_action above, or the Fake_Action directive, or by
         the linker).  At present just a hook for some tracing code.          */
 
-    if (printprops_switch)
+    if (printactions_switch)
         printf("Action '%s' is numbered %d\n",b,c);
 }
 
@@ -155,10 +336,10 @@ extern void make_fake_action(void)
     snprintf(action_sub, MAX_IDENTIFIER_LENGTH+4, "%s__A", token_text);
     i = symbol_index(action_sub, -1);
 
-    if (!(sflags[i] & UNKNOWN_SFLAG))
+    if (!(symbols[i].flags & UNKNOWN_SFLAG))
     {   discard_token_location(beginning_debug_location);
         /* The user didn't know they were defining FOO__A, but they were and it's a problem. */
-        ebf_symbol_error("new fake action name", action_sub, typename(stypes[i]), slines[i]);
+        ebf_symbol_error("new fake action name", action_sub, typename(symbols[i].type), symbols[i].line);
         panic_mode_error_recovery(); return;
     }
 
@@ -170,7 +351,7 @@ extern void make_fake_action(void)
     if (debugfile_switch)
     {   debug_file_printf("<fake-action>");
         debug_file_printf("<identifier>##%s</identifier>", token_text);
-        debug_file_printf("<value>%d</value>", svals[i]);
+        debug_file_printf("<value>%d</value>", symbols[i].value);
         get_next_token();
         write_debug_locations
             (get_token_location_end(beginning_debug_location));
@@ -193,33 +374,34 @@ extern assembly_operand action_of_name(char *name)
     snprintf(action_sub, MAX_IDENTIFIER_LENGTH+4, "%s__A", name);
     j = symbol_index(action_sub, -1);
 
-    if (stypes[j] == FAKE_ACTION_T)
+    if (symbols[j].type == FAKE_ACTION_T)
     {   INITAO(&AO);
-        AO.value = svals[j];
+        AO.value = symbols[j].value;
         if (!glulx_mode)
           AO.type = LONG_CONSTANT_OT;
         else
           set_constant_ot(&AO);
-        sflags[j] |= USED_SFLAG;
+        symbols[j].flags |= USED_SFLAG;
         return AO;
     }
 
-    if (sflags[j] & UNKNOWN_SFLAG)
+    if (symbols[j].flags & UNKNOWN_SFLAG)
     {
-        if (no_actions>=MAX_ACTIONS) memoryerror("MAX_ACTIONS",MAX_ACTIONS);
+        ensure_memory_list_available(&actions_memlist, no_actions+1);
         new_action(name, no_actions);
-        action_symbol[no_actions] = j;
+        actions[no_actions].symbol = j;
+        actions[no_actions].byte_offset = 0; /* fill in later */
         assign_symbol(j, no_actions++, CONSTANT_T);
-        sflags[j] |= ACTION_SFLAG;
+        symbols[j].flags |= ACTION_SFLAG;
     }
-    sflags[j] |= USED_SFLAG;
+    symbols[j].flags |= USED_SFLAG;
 
     INITAO(&AO);
-    AO.value = svals[j];
+    AO.value = symbols[j].value;
     AO.marker = ACTION_MV;
     if (!glulx_mode) {
       AO.type = (module_switch)?LONG_CONSTANT_OT:SHORT_CONSTANT_OT;
-      if (svals[j] >= 256) AO.type = LONG_CONSTANT_OT;
+      if (symbols[j].value >= 256) AO.type = LONG_CONSTANT_OT;
     }
     else {
       AO.type = CONSTANT_OT;
@@ -233,27 +415,27 @@ extern void find_the_actions(void)
     char action_sub[MAX_IDENTIFIER_LENGTH+4];
 
     if (module_switch)
-        for (i=0; i<no_actions; i++) action_byte_offset[i] = 0;
+        for (i=0; i<no_actions; i++) actions[i].byte_offset = 0;
     else
     for (i=0; i<no_actions; i++)
-    {   strcpy(action_name, (char *) symbs[action_symbol[i]]);
+    {   strcpy(action_name, symbols[actions[i].symbol].name);
         action_name[strlen(action_name) - 3] = '\0'; /* remove "__A" */
         strcpy(action_sub, action_name);
         strcat(action_sub, "Sub");
         j = symbol_index(action_sub, -1);
-        if (sflags[j] & UNKNOWN_SFLAG)
+        if (symbols[j].flags & UNKNOWN_SFLAG)
         {
-            error_named_at("No ...Sub action routine found for action:", action_name, slines[action_symbol[i]]);
+            error_named_at("No ...Sub action routine found for action:", action_name, symbols[actions[i].symbol].line);
         }
         else
-        if (stypes[j] != ROUTINE_T)
+        if (symbols[j].type != ROUTINE_T)
         {
-            error_named_at("No ...Sub action routine found for action:", action_name, slines[action_symbol[i]]);
-            error_named_at("-- ...Sub symbol found, but not a routine:", action_sub, slines[j]);
+            error_named_at("No ...Sub action routine found for action:", action_name, symbols[actions[i].symbol].line);
+            error_named_at("-- ...Sub symbol found, but not a routine:", action_sub, symbols[j].line);
         }
         else
-        {   action_byte_offset[i] = svals[j];
-            sflags[j] |= USED_SFLAG;
+        {   actions[i].byte_offset = symbols[j].value;
+            symbols[j].flags |= USED_SFLAG;
         }
     }
 }
@@ -276,8 +458,17 @@ static int make_adjective(char *English_word)
     int i; 
     uchar new_sort_code[MAX_DICT_WORD_BYTES];
 
-    if (no_adjectives >= MAX_ADJECTIVES)
-        memoryerror("MAX_ADJECTIVES", MAX_ADJECTIVES);
+    if (no_adjectives >= 255) {
+        error("Grammar version 1 cannot support more than 255 prepositions");
+        return 0;
+    }
+    if (ZCODE_LESS_DICT_DATA && !glulx_mode) {
+        /* We need to use #dict_par3 for the preposition number. */
+        error("Grammar version 1 cannot be used with ZCODE_LESS_DICT_DATA");
+        return 0;
+    }
+    ensure_memory_list_available(&adjectives_memlist, no_adjectives+1);
+    ensure_memory_list_available(&adjective_sort_code_memlist, (no_adjectives+1) * DICT_WORD_BYTES);
 
     dictionary_prepare(English_word, new_sort_code);
     for (i=0; i<no_adjectives; i++)
@@ -305,7 +496,9 @@ static int make_parsing_routine(int32 routine_address)
         if (grammar_token_routine[l] == routine_address)
             return l;
 
-    grammar_token_routine[l] = routine_address;
+    ensure_memory_list_available(&grammar_token_routine_memlist, no_grammar_token_routines+1);
+    
+    grammar_token_routine[no_grammar_token_routines] = routine_address;
     return(no_grammar_token_routines++);
 }
 
@@ -327,7 +520,7 @@ static int find_or_renumber_verb(char *English_verb, int *new_number)
 
     char *p;
     p=English_verb_list;
-    while (p < English_verb_list_top)
+    while (p < English_verb_list+English_verb_list_size)
     {   if (strcmp(English_verb, p+3) == 0)
         {   if (new_number)
             {   p[1] = (*new_number)/256;
@@ -341,10 +534,27 @@ static int find_or_renumber_verb(char *English_verb, int *new_number)
     return(-1);
 }
 
+static char *find_verb_by_number(int num)
+{
+    /*  Find the English verb string with the given verb number. */
+    char *p;
+    p=English_verb_list;
+    while (p < English_verb_list+English_verb_list_size)
+    {
+        int val = (p[1] << 8) | p[2];
+        if (val == num) {
+            return p+3;
+        }
+        p=p+(uchar)p[0];
+    }
+    return "???";
+}
+
 static void register_verb(char *English_verb, int number)
 {
     /*  Registers a new English verb as referring to the given Inform-verb
         number.  (See comments above for format of the list.)                */
+    char *top;
     int entrysize;
 
     if (find_or_renumber_verb(English_verb, NULL) != -1)
@@ -359,15 +569,14 @@ static void register_verb(char *English_verb, int number)
     entrysize = strlen(English_verb)+4;
     if (entrysize > MAX_VERB_WORD_SIZE+4)
         error_numbered("Verb word is too long -- max length is", MAX_VERB_WORD_SIZE);
+    ensure_memory_list_available(&English_verb_list_memlist, English_verb_list_size + entrysize);
+    top = English_verb_list + English_verb_list_size;
     English_verb_list_size += entrysize;
-    if (English_verb_list_size >= MAX_VERBSPACE)
-        memoryerror("MAX_VERBSPACE", MAX_VERBSPACE);
-
-    English_verb_list_top[0] = entrysize;
-    English_verb_list_top[1] = number/256;
-    English_verb_list_top[2] = number%256;
-    strcpy(English_verb_list_top+3, English_verb);
-    English_verb_list_top += entrysize;
+
+    top[0] = entrysize;
+    top[1] = number/256;
+    top[2] = number%256;
+    strcpy(top+3, English_verb);
 }
 
 static int get_verb(void)
@@ -395,9 +604,25 @@ static int get_verb(void)
 /*   Grammar lines for Verb/Extend directives.                               */
 /* ------------------------------------------------------------------------- */
 
+static void ensure_grammar_lines_available(int verbnum, int num)
+{
+    /* Note that the size field always starts positive. */
+    if (num > Inform_verbs[verbnum].size) {
+        int newsize = 2*num+4;
+        my_realloc(&Inform_verbs[verbnum].l, sizeof(int) * Inform_verbs[verbnum].size, sizeof(int) * newsize, "grammar lines for one verb");
+        Inform_verbs[verbnum].size = newsize;
+    }
+}
+
 static int grammar_line(int verbnum, int line)
 {
-    /*  Parse a grammar line, to be written into grammar_lines[mark] onward.
+    /*  Parse a grammar line, to be written into grammar_lines[] starting
+        at grammar_lines_top. grammar_lines_top is left at the end
+        of the new line.
+
+        This stores the line position in Inform_verbs[verbnum].l[line].
+        (It does not increment Inform_verbs[verbnum].lines; the caller
+        must do that.)
 
         Syntax: * <token1> ... <token-n> -> <action>
 
@@ -435,32 +660,9 @@ static int grammar_line(int verbnum, int line)
         return FALSE;
     }
 
-    /*  Have we run out of lines or token space?  */
-
-    if (line >= MAX_LINES_PER_VERB)
-    {   discard_token_location(beginning_debug_location);
-        error("Too many lines of grammar for verb. This maximum is built \
-into Inform, so suggest rewriting grammar using general parsing routines");
-        return(FALSE);
-    }
-
-    /*  Internally, a line can be up to 3*32 + 1 + 2 = 99 bytes long  */
-    /*  In Glulx, that's 5*32 + 4 = 164 bytes */
-
     mark = grammar_lines_top;
-    if (!glulx_mode) {
-        if (mark + 100 >= MAX_LINESPACE)
-        {   discard_token_location(beginning_debug_location);
-            memoryerror("MAX_LINESPACE", MAX_LINESPACE);
-        }
-    }
-    else {
-        if (mark + 165 >= MAX_LINESPACE)
-        {   discard_token_location(beginning_debug_location);
-            memoryerror("MAX_LINESPACE", MAX_LINESPACE);
-        }
-    }
 
+    ensure_grammar_lines_available(verbnum, line+1);
     Inform_verbs[verbnum].l[line] = mark;
 
     if (!glulx_mode) {
@@ -471,6 +673,7 @@ into Inform, so suggest rewriting grammar using general parsing routines");
         mark = mark + 3;
         TOKEN_SIZE = 5;
     }
+    ensure_memory_list_available(&grammar_lines_memlist, mark);
 
     grammar_token = 0; last_was_slash = TRUE; slash_mode = FALSE;
     no_grammar_lines++;
@@ -522,7 +725,7 @@ into Inform, so suggest rewriting grammar using general parsing routines");
 
                      get_next_token();
                      if ((token_type != SYMBOL_TT)
-                         || (stypes[token_value] != ROUTINE_T))
+                         || (symbols[token_value].type != ROUTINE_T))
                      {   discard_token_location(beginning_debug_location);
                          ebf_error("routine name after 'noun='", token_text);
                          panic_mode_error_recovery();
@@ -530,12 +733,12 @@ into Inform, so suggest rewriting grammar using general parsing routines");
                      }
                      if (grammar_version_number == 1)
                          bytecode
-                             = 16 + make_parsing_routine(svals[token_value]);
+                             = 16 + make_parsing_routine(symbols[token_value].value);
                      else
                      {   bytecode = 0x83;
-                         wordcode = svals[token_value];
+                         wordcode = symbols[token_value].value;
                      }
-                     sflags[token_value] |= USED_SFLAG;
+                     symbols[token_value].flags |= USED_SFLAG;
                  }
                  else
                  {   put_token_back();
@@ -586,7 +789,7 @@ are using Library 6/3 or later");
 
                  get_next_token();
                  if ((token_type != SYMBOL_TT)
-                     || (stypes[token_value] != ROUTINE_T))
+                     || (symbols[token_value].type != ROUTINE_T))
                  {   discard_token_location(beginning_debug_location);
                      ebf_error("routine name after 'scope='", token_text);
                      panic_mode_error_recovery();
@@ -595,9 +798,9 @@ are using Library 6/3 or later");
 
                  if (grammar_version_number == 1)
                      bytecode = 80 +
-                         make_parsing_routine(svals[token_value]);
-                 else { bytecode = 0x85; wordcode = svals[token_value]; }
-                 sflags[token_value] |= USED_SFLAG;
+                         make_parsing_routine(symbols[token_value].value);
+                 else { bytecode = 0x85; wordcode = symbols[token_value].value; }
+                 symbols[token_value].flags |= USED_SFLAG;
              }
         else if ((token_type == SEP_TT) && (token_value == SETEQUALS_SEP))
              {   discard_token_location(beginning_debug_location);
@@ -608,25 +811,25 @@ are using Library 6/3 or later");
         else {   /*  <attribute>  or  <general-parsing-routine>  tokens      */
 
                  if ((token_type != SYMBOL_TT)
-                     || ((stypes[token_value] != ATTRIBUTE_T)
-                         && (stypes[token_value] != ROUTINE_T)))
+                     || ((symbols[token_value].type != ATTRIBUTE_T)
+                         && (symbols[token_value].type != ROUTINE_T)))
                  {   discard_token_location(beginning_debug_location);
                      error_named("No such grammar token as", token_text);
                      panic_mode_error_recovery();
                      return FALSE;
                  }
-                 if (stypes[token_value]==ATTRIBUTE_T)
+                 if (symbols[token_value].type==ATTRIBUTE_T)
                  {   if (grammar_version_number == 1)
-                         bytecode = 128 + svals[token_value];
-                     else { bytecode = 4; wordcode = svals[token_value]; }
+                         bytecode = 128 + symbols[token_value].value;
+                     else { bytecode = 4; wordcode = symbols[token_value].value; }
                  }
                  else
                  {   if (grammar_version_number == 1)
                          bytecode = 48 +
-                             make_parsing_routine(svals[token_value]);
-                     else { bytecode = 0x86; wordcode = svals[token_value]; }
+                             make_parsing_routine(symbols[token_value].value);
+                     else { bytecode = 0x86; wordcode = symbols[token_value].value; }
                  }
-                 sflags[token_value] |= USED_SFLAG;
+                 symbols[token_value].flags |= USED_SFLAG;
              }
 
         grammar_token++; no_grammar_tokens++;
@@ -641,6 +844,7 @@ tokens in any line (unless you're compiling with library 6/3 or later)");
                     error("'/' can only be applied to prepositions");
                 bytecode |= 0x10;
             }
+            ensure_memory_list_available(&grammar_lines_memlist, mark+5);
             grammar_lines[mark++] = bytecode;
             if (!glulx_mode) {
                 grammar_lines[mark++] = wordcode/256;
@@ -656,6 +860,7 @@ tokens in any line (unless you're compiling with library 6/3 or later)");
 
     } while (TRUE);
 
+    ensure_memory_list_available(&grammar_lines_memlist, mark+1);
     grammar_lines[mark++] = 15;
     grammar_lines_top = mark;
 
@@ -702,6 +907,7 @@ Library 6/3 or later");
         debug_file_printf("</table-entry>");
     }
 
+    ensure_memory_list_available(&grammar_lines_memlist, mark+3);
     if (!glulx_mode) {
         if (reverse_action)
             j = j + 0x400;
@@ -731,8 +937,8 @@ extern void make_verb(void)
 
     int Inform_verb, meta_verb_flag=FALSE, verb_equals_form=FALSE;
 
-    char *English_verbs_given[MAX_VERB_SYNONYMS];
-    int no_given = 0, i;
+    int no_given = 0, verbs_given_pos = 0;
+    int i, pos;
 
     directive_keywords.enabled = TRUE;
 
@@ -745,11 +951,11 @@ extern void make_verb(void)
 
     while ((token_type == DQ_TT) || (token_type == SQ_TT))
     {
-        if (no_given >= MAX_VERB_SYNONYMS) {
-            error("Too many synonyms in a Verb directive.");
-            panic_mode_error_recovery(); return;
-        }
-        English_verbs_given[no_given++] = token_text;
+        int len = strlen(token_text) + 1;
+        ensure_memory_list_available(&English_verbs_given_memlist, verbs_given_pos + len);
+        strcpy(English_verbs_given+verbs_given_pos, token_text);
+        verbs_given_pos += len;
+        no_given++;
         get_next_token();
     }
 
@@ -768,16 +974,25 @@ extern void make_verb(void)
             ebf_error("';' after English verb", token_text);
     }
     else
-    {   Inform_verb = no_Inform_verbs;
-        if (no_Inform_verbs == MAX_VERBS)
-            memoryerror("MAX_VERBS",MAX_VERBS);
+    {   verb_equals_form = FALSE;
+        if (!glulx_mode && no_Inform_verbs >= 255) {
+            error("Z-code is limited to 255 verbs.");
+            panic_mode_error_recovery(); return;
+        }
+        ensure_memory_list_available(&Inform_verbs_memlist, no_Inform_verbs+1);
+        Inform_verb = no_Inform_verbs;
+        Inform_verbs[no_Inform_verbs].lines = 0;
+        Inform_verbs[no_Inform_verbs].size = 4;
+        Inform_verbs[no_Inform_verbs].l = my_malloc(sizeof(int) * Inform_verbs[no_Inform_verbs].size, "grammar lines for one verb");
     }
 
-    for (i=0; i<no_given; i++)
-    {   dictionary_add(English_verbs_given[i],
+    for (i=0, pos=0; i<no_given; i++) {
+        char *wd = English_verbs_given+pos;
+        dictionary_add(wd,
             0x41 + ((meta_verb_flag)?0x02:0x00),
             (glulx_mode)?(0xffff-Inform_verb):(0xff-Inform_verb), 0);
-        register_verb(English_verbs_given[i], Inform_verb);
+        register_verb(wd, Inform_verb);
+        pos += (strlen(wd) + 1);
     }
 
     if (!verb_equals_form)
@@ -815,9 +1030,13 @@ extern void extend_verb(void)
 
     get_next_token();
     if ((token_type == DIR_KEYWORD_TT) && (token_value == ONLY_DK))
-    {   l = -1;
-        if (no_Inform_verbs == MAX_VERBS)
-            memoryerror("MAX_VERBS", MAX_VERBS);
+    {
+        if (!glulx_mode && no_Inform_verbs >= 255) {
+            error("Z-code is limited to 255 verbs.");
+            panic_mode_error_recovery(); return;
+        }
+        ensure_memory_list_available(&Inform_verbs_memlist, no_Inform_verbs+1);
+        l = -1;
         while (get_next_token(),
                ((token_type == DQ_TT) || (token_type == SQ_TT)))
         {   Inform_verb = get_verb();
@@ -836,8 +1055,15 @@ extern void extend_verb(void)
         /*  Copy the old Inform-verb into a new one which the list of
             English-verbs given have had their dictionary entries modified
             to point to                                                      */
+        /*  (We are copying entry Inform_verb to no_Inform_verbs here.) */
 
-        Inform_verbs[no_Inform_verbs] = Inform_verbs[Inform_verb];
+        l = Inform_verbs[Inform_verb].lines; /* number of lines to copy */
+        
+        Inform_verbs[no_Inform_verbs].lines = l;
+        Inform_verbs[no_Inform_verbs].size = l+4;
+        Inform_verbs[no_Inform_verbs].l = my_malloc(sizeof(int) * Inform_verbs[no_Inform_verbs].size, "grammar lines for one verb");
+        for (k=0; k<l; k++)
+            Inform_verbs[no_Inform_verbs].l[k] = Inform_verbs[Inform_verb].l[k];
         Inform_verb = no_Inform_verbs++;
     }
     else
@@ -870,17 +1096,23 @@ extern void extend_verb(void)
     lines = 0;
     if (extend_mode == EXTEND_LAST) lines=l;
     do
-    {   if (extend_mode == EXTEND_FIRST)
+    {
+        if (extend_mode == EXTEND_FIRST) {
+            ensure_grammar_lines_available(Inform_verb, l+lines+1);
             for (k=l; k>0; k--)
                  Inform_verbs[Inform_verb].l[k+lines]
                      = Inform_verbs[Inform_verb].l[k-1+lines];
+        }
     } while (grammar_line(Inform_verb, lines++));
 
     if (extend_mode == EXTEND_FIRST)
-    {   Inform_verbs[Inform_verb].lines = l+lines-1;
-        for (k=0; k<l; k++)
+    {
+        ensure_grammar_lines_available(Inform_verb, l+lines+1);
+        Inform_verbs[Inform_verb].lines = l+lines-1;
+        for (k=0; k<l; k++) {
             Inform_verbs[Inform_verb].l[k+lines-1]
                 = Inform_verbs[Inform_verb].l[k+lines];
+        }
     }
     else Inform_verbs[Inform_verb].lines = --lines;
 
@@ -901,11 +1133,13 @@ extern void init_verbs_vars(void)
     English_verb_list_size = 0;
 
     Inform_verbs = NULL;
-    action_byte_offset = NULL;
+    actions = NULL;
+    grammar_lines = NULL;
     grammar_token_routine = NULL;
     adjectives = NULL;
     adjective_sort_code = NULL;
     English_verb_list = NULL;
+    English_verbs_given = NULL;
 
     if (!glulx_mode)
         grammar_version_number = 1;
@@ -925,32 +1159,53 @@ extern void verbs_begin_pass(void)
 
 extern void verbs_allocate_arrays(void)
 {
-    Inform_verbs          = my_calloc(sizeof(verbt),   MAX_VERBS, "verbs");
-    grammar_lines         = my_malloc(MAX_LINESPACE, "grammar lines");
-    action_byte_offset    = my_calloc(sizeof(int32),   MAX_ACTIONS, "actions");
-    action_symbol         = my_calloc(sizeof(int32),   MAX_ACTIONS,
-                                "action symbols");
-    grammar_token_routine = my_calloc(sizeof(int32),   MAX_ACTIONS,
-                                "grammar token routines");
-    adjectives            = my_calloc(sizeof(int32),   MAX_ADJECTIVES,
-                                "adjectives");
-    adjective_sort_code   = my_calloc(DICT_WORD_BYTES, MAX_ADJECTIVES,
-                                "adjective sort codes");
-
-    English_verb_list     = my_malloc(MAX_VERBSPACE, "register of verbs");
-    English_verb_list_top = English_verb_list;
+    initialise_memory_list(&Inform_verbs_memlist,
+        sizeof(verbt), 128, (void**)&Inform_verbs,
+        "verbs");
+    
+    initialise_memory_list(&grammar_lines_memlist,
+        sizeof(uchar), 4000, (void**)&grammar_lines,
+        "grammar lines");
+    
+    initialise_memory_list(&actions_memlist,
+        sizeof(actioninfo), 128, (void**)&actions,
+        "actions");
+    
+    initialise_memory_list(&grammar_token_routine_memlist,
+        sizeof(int32), 50, (void**)&grammar_token_routine,
+        "grammar token routines");
+
+    initialise_memory_list(&adjectives_memlist,
+        sizeof(int32), 50, (void**)&adjectives,
+        "adjectives");
+    initialise_memory_list(&adjective_sort_code_memlist,
+        sizeof(uchar), 50*DICT_WORD_BYTES, (void**)&adjective_sort_code,
+        "adjective sort codes");
+
+    initialise_memory_list(&English_verb_list_memlist,
+        sizeof(char), 2048, (void**)&English_verb_list,
+        "register of verbs");
+
+    initialise_memory_list(&English_verbs_given_memlist,
+        sizeof(char), 80, (void**)&English_verbs_given,
+        "verb words within a single definition");
 }
 
 extern void verbs_free_arrays(void)
 {
-    my_free(&Inform_verbs, "verbs");
-    my_free(&grammar_lines, "grammar lines");
-    my_free(&action_byte_offset, "actions");
-    my_free(&action_symbol, "action symbols");
-    my_free(&grammar_token_routine, "grammar token routines");
-    my_free(&adjectives, "adjectives");
-    my_free(&adjective_sort_code, "adjective sort codes");
-    my_free(&English_verb_list, "register of verbs");
+    int ix;
+    for (ix=0; ix<no_Inform_verbs; ix++)
+    {
+        my_free(&Inform_verbs[ix].l, "grammar lines for one verb");
+    }
+    deallocate_memory_list(&Inform_verbs_memlist);
+    deallocate_memory_list(&grammar_lines_memlist);
+    deallocate_memory_list(&actions_memlist);
+    deallocate_memory_list(&grammar_token_routine_memlist);
+    deallocate_memory_list(&adjectives_memlist);
+    deallocate_memory_list(&adjective_sort_code_memlist);
+    deallocate_memory_list(&English_verb_list_memlist);
+    deallocate_memory_list(&English_verbs_given_memlist);
 }
 
 /* ========================================================================= */