Update to Inform v6.42
[inform.git] / src / verbs.c
index 2caa67cb4d4e2998b042299c0a50d3c100ffdcda..de35f3039dd6ceefebc483d94a5e59b434da9536 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.42                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2024                                 */
 /*                                                                           */
 /* 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      */
@@ -61,6 +56,8 @@ int no_adjectives;                     /* Number of adjectives made so far   */
 /*           for example the English verbs "take" and "drop" both normally   */
 /*           correspond in a game's dictionary to the same Inform verb.  An  */
 /*           Inform verb is essentially a list of grammar lines.             */
+/*           (Calling them "English verbs" is of course out of date. Read    */
+/*           this as jargon for "dict words which are verbs".                */
 /* ------------------------------------------------------------------------- */
 /*   Arrays defined below:                                                   */
 /*                                                                           */
@@ -81,39 +78,230 @@ 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          */
+
+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, except it's sometimes no_adjectives+1 because we can bump it tentatively */
+  static memory_list adjective_sort_code_memlist;
+
+  static memory_list action_symname_memlist; /* Used for temporary symbols */
 
 /* ------------------------------------------------------------------------- */
 /*   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;
+            }
+        }
+    }
 }
 
 /* ------------------------------------------------------------------------- */
@@ -123,37 +311,43 @@ extern void list_verb_table(void)
 static void new_action(char *b, int c)
 {
     /*  Called whenever a new action (or fake action) is created (either
-        by using make_action above, or the Fake_Action directive, or by
-        the linker).  At present just a hook for some tracing code.          */
+        by using make_action above, or the Fake_Action directive).
+        At present just a hook for some tracing code.                        */
 
-    if (printprops_switch)
-        printf("Action '%s' is numbered %d\n",b,c);
+    if (printactions_switch)
+        printf("%s: Action '%s' is numbered %d\n", current_location_text(), b, c);
 }
 
 /* Note that fake actions are numbered from a high base point upwards;
    real actions are numbered from 0 upward in GV2.                           */
 
 extern void make_fake_action(void)
-{   int i;
-    char action_sub[MAX_IDENTIFIER_LENGTH+4];
+{   char *action_sub;
+    int i;
     debug_location_beginning beginning_debug_location =
         get_token_location_beginning();
 
     get_next_token();
     if (token_type != SYMBOL_TT)
     {   discard_token_location(beginning_debug_location);
-        ebf_error("new fake action name", token_text);
+        ebf_curtoken_error("new fake action name");
         panic_mode_error_recovery(); return;
     }
+
+    /* Enough space for "token__A". */
+    ensure_memory_list_available(&action_symname_memlist, strlen(token_text)+4);
+    action_sub = action_symname_memlist.data;
+    strcpy(action_sub, token_text);
+    strcat(action_sub, "__A");
+    
     /* Action symbols (including fake_actions) may collide with other kinds of symbols. So we don't check that. */
 
-    snprintf(action_sub, MAX_IDENTIFIER_LENGTH+4, "%s__A", token_text);
-    i = symbol_index(action_sub, -1);
+    i = symbol_index(action_sub, -1, NULL);
 
-    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;
     }
 
@@ -165,7 +359,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));
@@ -181,40 +375,46 @@ extern assembly_operand action_of_name(char *name)
     /*  Returns the action number of the given name, creating it as a new
         action name if it isn't already known as such.                       */
 
-    char action_sub[MAX_IDENTIFIER_LENGTH+4];
+    char *action_sub;
     int j;
     assembly_operand AO;
 
-    snprintf(action_sub, MAX_IDENTIFIER_LENGTH+4, "%s__A", name);
-    j = symbol_index(action_sub, -1);
+    /* Enough space for "name__A". */
+    ensure_memory_list_available(&action_symname_memlist, strlen(name)+4);
+    action_sub = action_symname_memlist.data;
+    strcpy(action_sub, name);
+    strcat(action_sub, "__A");
+    
+    j = symbol_index(action_sub, -1, NULL);
 
-    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;
+      AO.type = SHORT_CONSTANT_OT;
+      if (symbols[j].value >= 256) AO.type = LONG_CONSTANT_OT;
     }
     else {
       AO.type = CONSTANT_OT;
@@ -224,31 +424,33 @@ extern assembly_operand action_of_name(char *name)
 
 extern void find_the_actions(void)
 {   int i; int32 j;
-    char action_name[MAX_IDENTIFIER_LENGTH+4];
-    char action_sub[MAX_IDENTIFIER_LENGTH+4];
 
-    if (module_switch)
-        for (i=0; i<no_actions; i++) action_byte_offset[i] = 0;
-    else
     for (i=0; i<no_actions; i++)
-    {   strcpy(action_name, (char *) symbs[action_symbol[i]]);
-        action_name[strlen(action_name) - 3] = '\0'; /* remove "__A" */
+    {
+        /* The name looks like "action__A". We're going to convert that to
+           "actionSub". Allocate enough space for both. */
+        int namelen = strlen(symbols[actions[i].symbol].name);
+        char *action_sub, *action_name;
+        ensure_memory_list_available(&action_symname_memlist, 2*(namelen+1));
+        action_sub = action_symname_memlist.data;
+        action_name = (char *)action_symname_memlist.data + (namelen+1);
+        
+        strcpy(action_name, symbols[actions[i].symbol].name);
+        action_name[namelen - 3] = '\0'; /* remove "__A" */
         strcpy(action_sub, action_name);
         strcat(action_sub, "Sub");
-        j = symbol_index(action_sub, -1);
-        if (sflags[j] & UNKNOWN_SFLAG)
+        j = symbol_index(action_sub, -1, NULL);
+        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)
+        else 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]);
+            ebf_symbol_error("action's ...Sub routine", action_sub, typename(symbols[j].type), 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;
         }
     }
 }
@@ -268,12 +470,25 @@ static int make_adjective(char *English_word)
         This routine is used only in grammar version 1: the corresponding
         table is left empty in GV2.                                          */
 
+    uchar *new_sort_code;
     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;
+    }
 
+    /* Allocate the extra space even though we might not need it. We'll use
+       the prospective new adjective_sort_code slot as a workspace. */
+    ensure_memory_list_available(&adjectives_memlist, no_adjectives+1);
+    ensure_memory_list_available(&adjective_sort_code_memlist, (no_adjectives+1) * DICT_WORD_BYTES);
+
+    new_sort_code = adjective_sort_code+no_adjectives*DICT_WORD_BYTES;
     dictionary_prepare(English_word, new_sort_code);
     for (i=0; i<no_adjectives; i++)
         if (compare_sorts(new_sort_code,
@@ -281,8 +496,6 @@ static int make_adjective(char *English_word)
             return(0xff-i);
     adjectives[no_adjectives]
         = dictionary_add(English_word,8,0,0xff-no_adjectives);
-    copy_sorts(adjective_sort_code+no_adjectives*DICT_WORD_BYTES,
-        new_sort_code);
     return(0xff-no_adjectives++);
 }
 
@@ -300,7 +513,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++);
 }
 
@@ -322,7 +537,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;
@@ -336,10 +551,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 = ((uchar)p[1] << 8) | (uchar)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)
@@ -349,20 +581,18 @@ static void register_verb(char *English_verb, int number)
 
     /* We set a hard limit of MAX_VERB_WORD_SIZE=120 because the
        English_verb_list table stores length in a leading byte. (We could
-       raise that to 250, really, but there's little point when
-       MAX_DICT_WORD_SIZE is 40.) */
+       raise that to 250, really.) */
     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);
+        error_fmt("Verb word is too long -- max length is %d", 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)
@@ -381,18 +611,67 @@ static int get_verb(void)
         return j;
     }
 
-    ebf_error("an English verb in quotes", token_text);
+    ebf_curtoken_error("an English verb in quotes");
 
     return -1;
 }
 
+void locate_dead_grammar_lines()
+{
+    /* Run through the grammar table and check whether each entry is
+       associated with a verb word. (Some might have been detached by
+       "Extend only".)
+    */
+    int verb;
+    char *p;
+
+    for (verb=0; verb<no_Inform_verbs; verb++) {
+        Inform_verbs[verb].used = FALSE;
+    }
+    
+    p=English_verb_list;
+    while (p < English_verb_list+English_verb_list_size)
+    {
+        verb = ((uchar)p[1] << 8) | (uchar)p[2];
+        if (verb < 0 || verb >= no_Inform_verbs) {
+            error_named("An entry in the English verb list had an invalid verb number", p+3);
+        }
+        else {
+            Inform_verbs[verb].used = TRUE;
+        }
+        p=p+(uchar)p[0];
+    }
+
+    for (verb=0; verb<no_Inform_verbs; verb++) {
+        if (!Inform_verbs[verb].used) {
+            warning_at("Verb declaration no longer has any verbs associated. Use \"Extend replace\" instead of \"Extend only\"?", Inform_verbs[verb].line);
+        }
+    }
+}
+
 /* ------------------------------------------------------------------------- */
 /*   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>
 
@@ -425,37 +704,14 @@ static int grammar_line(int verbnum, int line)
     }
     if (!((token_type == SEP_TT) && (token_value == TIMES_SEP)))
     {   discard_token_location(beginning_debug_location);
-        ebf_error("'*' divider", token_text);
+        ebf_curtoken_error("'*' divider");
         panic_mode_error_recovery();
         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) {
@@ -466,6 +722,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++;
@@ -475,12 +732,12 @@ into Inform, so suggest rewriting grammar using general parsing routines");
         bytecode = 0; wordcode = 0;
         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
         {   discard_token_location(beginning_debug_location);
-            ebf_error("'->' clause", token_text);
+            ebf_curtoken_error("'->' clause");
             return FALSE;
         }
         if ((token_type == SEP_TT) && (token_value == ARROW_SEP))
         {   if (last_was_slash && (grammar_token>0))
-                ebf_error("grammar token", token_text);
+                ebf_curtoken_error("grammar token");
             break;
         }
 
@@ -489,7 +746,7 @@ into Inform, so suggest rewriting grammar using general parsing routines");
         {   if (grammar_version_number == 1)
                 error("'/' can only be used with Library 6/3 or later");
             if (last_was_slash)
-                ebf_error("grammar token or '->'", token_text);
+                ebf_curtoken_error("grammar token or '->'");
             else
             {   last_was_slash = TRUE;
                 slash_mode = TRUE;
@@ -517,20 +774,20 @@ 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);
+                         ebf_curtoken_error("routine name after 'noun='");
                          panic_mode_error_recovery();
                          return FALSE;
                      }
                      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();
@@ -574,25 +831,25 @@ are using Library 6/3 or later");
                  get_next_token();
                  if (!((token_type==SEP_TT)&&(token_value==SETEQUALS_SEP)))
                  {   discard_token_location(beginning_debug_location);
-                     ebf_error("'=' after 'scope'", token_text);
+                     ebf_curtoken_error("'=' after 'scope'");
                      panic_mode_error_recovery();
                      return FALSE;
                  }
 
                  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);
+                     ebf_curtoken_error("routine name after 'scope='");
                      panic_mode_error_recovery();
                      return FALSE;
                  }
 
                  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);
@@ -603,25 +860,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++;
@@ -636,6 +893,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;
@@ -651,6 +909,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;
 
@@ -658,9 +917,9 @@ tokens in any line (unless you're compiling with library 6/3 or later)");
     get_next_token();
     dont_enter_into_symbol_table = FALSE;
 
-    if (token_type != DQ_TT)
+    if (token_type != UQ_TT)
     {   discard_token_location(beginning_debug_location);
-        ebf_error("name of new or existing action", token_text);
+        ebf_curtoken_error("name of new or existing action");
         panic_mode_error_recovery();
         return FALSE;
     }
@@ -697,6 +956,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;
@@ -726,7 +986,8 @@ extern void make_verb(void)
 
     int Inform_verb, meta_verb_flag=FALSE, verb_equals_form=FALSE;
 
-    char *English_verbs_given[32]; int no_given = 0, i;
+    int no_given = 0, verbs_given_pos = 0;
+    int i, pos;
 
     directive_keywords.enabled = TRUE;
 
@@ -738,12 +999,17 @@ extern void make_verb(void)
     }
 
     while ((token_type == DQ_TT) || (token_type == SQ_TT))
-    {   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();
     }
 
     if (no_given == 0)
-    {   ebf_error("English verb in quotes", token_text);
+    {   ebf_curtoken_error("English verb in quotes");
         panic_mode_error_recovery(); return;
     }
 
@@ -754,19 +1020,34 @@ extern void make_verb(void)
         if (Inform_verb == -1) return;
         get_next_token();
         if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
-            ebf_error("';' after English verb", token_text);
+            ebf_curtoken_error("';' after English verb");
     }
     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;
+        }
+        if (no_Inform_verbs >= 65535) {
+            error("Inform is limited to 65535 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");
+        Inform_verbs[no_Inform_verbs].line = get_brief_location(&ErrorReport);
+        Inform_verbs[no_Inform_verbs].used = FALSE;
     }
 
-    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)
@@ -804,9 +1085,17 @@ 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;
+        }
+        if (no_Inform_verbs >= 65535) {
+            error("Inform is limited to 65535 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();
@@ -825,8 +1114,17 @@ 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_verbs[no_Inform_verbs].line = get_brief_location(&ErrorReport);
+        Inform_verbs[no_Inform_verbs].used = FALSE;
         Inform_verb = no_Inform_verbs++;
     }
     else
@@ -850,7 +1148,7 @@ extern void extend_verb(void)
             extend_mode = EXTEND_LAST;
 
         if (extend_mode==0)
-        {   ebf_error("'replace', 'last', 'first' or '*'", token_text);
+        {   ebf_curtoken_error("'replace', 'last', 'first' or '*'");
             extend_mode = EXTEND_LAST;
         }
     }
@@ -859,17 +1157,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;
 
@@ -890,11 +1194,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;
@@ -914,32 +1220,58 @@ 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(&action_symname_memlist,
+        sizeof(uchar), 32, NULL,
+        "action temporary symbols");
+    
+    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(&action_symname_memlist);
+    deallocate_memory_list(&English_verb_list_memlist);
+    deallocate_memory_list(&English_verbs_given_memlist);
 }
 
 /* ========================================================================= */