Update to Inform v6.42
[inform.git] / src / expressp.c
index c5f79bcfff89b5c2c7d3dfb344f1363acc6e0250..906eed160e4dd7810d609aadfe3828c5d524f3bc 100644 (file)
@@ -1,9 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "expressp" :  The expression parser                                     */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 /*   "expressp" :  The expression parser                                     */
 /*                                                                           */
-/* Copyright (c) Graham Nelson 1993 - 2020                                   */
-/*                                                                           */
-/* This file is part of Inform.                                              */
+/*   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      */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -66,9 +65,9 @@ static int comma_allowed, arrow_allowed, superclass_allowed,
            array_init_ambiguity, action_ambiguity,
            etoken_count, inserting_token, bracket_level;
 
            array_init_ambiguity, action_ambiguity,
            etoken_count, inserting_token, bracket_level;
 
-extern int *variable_usage;
+int system_function_usage[NUMBER_SYSTEM_FUNCTIONS];
 
 
-int system_function_usage[32];
+static void check_system_constant_available(int);
 
 static int get_next_etoken(void)
 {   int v, symbol = 0, mark_symbol_as_used = FALSE,
 
 static int get_next_etoken(void)
 {   int v, symbol = 0, mark_symbol_as_used = FALSE,
@@ -86,6 +85,7 @@ static int get_next_etoken(void)
         current_token.value = token_value;
         current_token.type = token_type;
         current_token.marker = 0;
         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;
     }
         current_token.symtype = 0;
         current_token.symflags = -1;
     }
@@ -93,7 +93,7 @@ static int get_next_etoken(void)
     switch(current_token.type)
     {   case LOCAL_VARIABLE_TT:
             current_token.type = VARIABLE_TT;
     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:
             break;
 
         case DQ_TT:
@@ -136,17 +136,17 @@ but not used as a value:", unicode);
 
             mark_symbol_as_used = TRUE;
 
 
             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. */
             {   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;
                     {   current_token.marker = SYMBOL_MV;
-                        if (module_switch) import_symbol(symbol);
                         v = symbol;
                         break;
                     }
                         v = symbol;
                         break;
                     }
@@ -158,7 +158,7 @@ but not used as a value:", unicode);
                 case OBJECT_T:
                 case CLASS_T:
                     /* All objects must be backpatched in Glulx. */
                 case OBJECT_T:
                 case CLASS_T:
                     /* All objects must be backpatched in Glulx. */
-                    if (module_switch || glulx_mode)
+                    if (glulx_mode)
                         current_token.marker = OBJECT_MV;
                     break;
                 case ARRAY_T:
                         current_token.marker = OBJECT_MV;
                     break;
                 case ARRAY_T:
@@ -168,12 +168,10 @@ but not used as a value:", unicode);
                     current_token.marker = STATIC_ARRAY_MV;
                     break;
                 case INDIVIDUAL_PROPERTY_T:
                     current_token.marker = STATIC_ARRAY_MV;
                     break;
                 case INDIVIDUAL_PROPERTY_T:
-                    if (module_switch) current_token.marker = IDENT_MV;
                     break;
                 case CONSTANT_T:
                     break;
                 case CONSTANT_T:
-                    if (sflags[symbol] & (UNKNOWN_SFLAG + CHANGE_SFLAG))
+                    if (symbols[symbol].flags & (UNKNOWN_SFLAG + CHANGE_SFLAG))
                     {   current_token.marker = SYMBOL_MV;
                     {   current_token.marker = SYMBOL_MV;
-                        if (module_switch) import_symbol(symbol);
                         v = symbol;
                     }
                     else current_token.marker = 0;
                         v = symbol;
                     }
                     else current_token.marker = 0;
@@ -185,7 +183,7 @@ but not used as a value:", unicode);
                     current_token.marker = 0;
                     break;
             }
                     current_token.marker = 0;
                     break;
             }
-            if (sflags[symbol] & SYSTEM_SFLAG)
+            if (symbols[symbol].flags & SYSTEM_SFLAG)
                 current_token.marker = 0;
 
             current_token.value = v;
                 current_token.marker = 0;
 
             current_token.value = v;
@@ -205,9 +203,9 @@ but not used as a value:", unicode);
                 else current_token.type = SMALL_NUMBER_TT;
             }
 
                 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;
             {   current_token.type = VARIABLE_TT;
-                variable_usage[current_token.value] = TRUE;
+                variables[current_token.value].usage = TRUE;
             }
             break;
 
             }
             break;
 
@@ -328,8 +326,8 @@ but not used as a value:", unicode);
 
                     current_token.text += 3;
                     current_token.type = SYMBOL_TT;
 
                     current_token.text += 3;
                     current_token.type = SYMBOL_TT;
-                    symbol = symbol_index(current_token.text, -1);
-                    if (stypes[symbol] != GLOBAL_VARIABLE_T) {
+                    symbol = get_symbol_index(current_token.text);
+                    if (symbol < 0 || symbols[symbol].type != GLOBAL_VARIABLE_T) {
                         ebf_error(
                         "global variable name after '#g$'",
                         current_token.text);
                         ebf_error(
                         "global variable name after '#g$'",
                         current_token.text);
@@ -339,7 +337,7 @@ but not used as a value:", unicode);
                         break;
                     }
                     mark_symbol_as_used = TRUE;
                         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)
                     current_token.marker = 0;
                     if (!glulx_mode) {
                         if (current_token.value >= 0x100)
@@ -380,7 +378,7 @@ but not used as a value:", unicode);
                         "'#r$Routine' can now be written just 'Routine'");
                     current_token.text += 3;
                     current_token.type = SYMBOL_TT;
                         "'#r$Routine' can now be written just 'Routine'");
                     current_token.text += 3;
                     current_token.type = SYMBOL_TT;
-                    current_token.value = symbol_index(current_token.text, -1);
+                    current_token.value = symbol_index(current_token.text, -1, NULL);
                     goto ReceiveSymbol;
 
                 case HASHWDOLLAR_SEP:
                     goto ReceiveSymbol;
 
                 case HASHWDOLLAR_SEP:
@@ -392,13 +390,14 @@ but not used as a value:", unicode);
                     get_next_token();
                     system_constants.enabled = FALSE;
                     if (token_type != SYSTEM_CONSTANT_TT)
                     get_next_token();
                     system_constants.enabled = FALSE;
                     if (token_type != SYSTEM_CONSTANT_TT)
-                    {   ebf_error(
-                        "'r$', 'n$', 'g$' or internal Inform constant name after '#'",
-                        token_text);
+                    {   ebf_curtoken_error(
+                        "'r$', 'n$', 'g$' or internal Inform constant name after '#'");
                         break;
                     }
                     else
                         break;
                     }
                     else
-                    {   current_token.type   = token_type;
+                    {
+                        check_system_constant_available(token_value);
+                        current_token.type   = token_type;
                         current_token.value  = token_value;
                         current_token.text   = token_text;
                         current_token.marker = INCON_MV;
                         current_token.value  = token_value;
                         current_token.text   = token_text;
                         current_token.marker = INCON_MV;
@@ -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);
     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;
             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);
             || (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
             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);
         if (expr_trace_level >= 3)
         {   printf("Expr token = '%s' ", current_token.text);
-            describe_token(current_token);
+            describe_token(&current_token);
             printf("\n");
         }
     }
             printf("\n");
         }
     }
@@ -476,36 +475,40 @@ but not used as a value:", unicode);
     return TRUE;
 }
 
     return TRUE;
 }
 
-/* --- Operator precedences ------------------------------------------------ */
+/* --- Operator precedences and error values-------------------------------- */
 
 #define LOWER_P   101
 #define EQUAL_P   102
 #define GREATER_P 103
 
 
 #define LOWER_P   101
 #define EQUAL_P   102
 #define GREATER_P 103
 
-#define e1        1       /* Missing operand error                */
-#define e2        2       /* Unexpected close bracket             */
-#define e3        3       /* Missing operator error               */
-#define e4        4       /* Expression ends with an open bracket */
-#define e5        5       /* Associativity illegal error          */
+#define BYPREC     -1       /* Compare the precedence of two operators */
+
+#define NOVAL_E     1       /* Missing operand error                */
+#define CLOSEB_E    2       /* Unexpected close bracket             */
+#define NOOP_E      3       /* Missing operator error               */
+#define OPENB_E     4       /* Expression ends with an open bracket */
+#define ASSOC_E     5       /* Associativity illegal error          */
 
 
-const int prec_table[] = {
+const int prec_table[49] = {
 
 
-/* a .......... (         )           end       op          term             */
+/*   a .......   (         )           end       op:pre      op:bin      op:post     term      */
 
 
-/* b  (    */   LOWER_P,  e3,         LOWER_P,  LOWER_P,    e3,
-/* .  )    */   EQUAL_P,  GREATER_P,  e2,       GREATER_P,  GREATER_P,
-/* .  end  */   e4,       GREATER_P,  e1,       GREATER_P,  GREATER_P,
-/* .  op   */   LOWER_P,  GREATER_P,  LOWER_P,  -1,         GREATER_P,
-/* .  term */   LOWER_P,  e3,         LOWER_P,  LOWER_P,    e3
+/* b  (    */    LOWER_P,  NOOP_E,     LOWER_P,  LOWER_P,    LOWER_P,    NOOP_E,     NOOP_E,
+/* .  )    */    EQUAL_P,  GREATER_P,  CLOSEB_E, GREATER_P,  GREATER_P,  GREATER_P,  GREATER_P,
+/* .  end  */    OPENB_E,  GREATER_P,  NOVAL_E,  GREATER_P,  GREATER_P,  GREATER_P,  GREATER_P,
+/* .  op:pre  */ LOWER_P,  NOOP_E,     LOWER_P,  BYPREC,     BYPREC,     NOOP_E,     NOOP_E,
+/* .  op:bin  */ LOWER_P,  GREATER_P,  LOWER_P,  BYPREC,     BYPREC,     BYPREC,     GREATER_P,
+/* .  op:post */ LOWER_P,  GREATER_P,  LOWER_P,  BYPREC,     BYPREC,     BYPREC,     GREATER_P,
+/* .  term */    LOWER_P,  NOOP_E,     LOWER_P,  LOWER_P,    LOWER_P,    NOOP_E,     NOOP_E
 
 };
 
 
 };
 
-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,
         the only possible values are GREATER_P, LOWER_P or EQUAL_P;
 {
     /*  We are comparing the precedence of tokens  a  and  b
         (where a occurs to the left of b).  If the expression is correct,
         the only possible values are GREATER_P, LOWER_P or EQUAL_P;
-        if it is malformed then one of e1 to e5 results.
+        if it is malformed then one of the *_E results.
 
         Note that this routine is not symmetrical and that the relation
         is not trichotomous.
 
         Note that this routine is not symmetrical and that the relation
         is not trichotomous.
@@ -516,29 +519,54 @@ static int find_prec(token_data a, token_data b)
             a GREATER_P a   if a left-associative
     */
 
             a GREATER_P a   if a left-associative
     */
 
-    int i, j, l1, l2;
+    int ai, bi, j, l1, l2;
 
 
-    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;
+    /*   Select a column and row in prec_table, based on the type of
+         a and b. If a/b is an operator, we have to distinguish three
+         columns/rows depending on whether the operator is prefix,
+         postfix, or neither.
+    */
+    
+    switch(a->type)
+    {   case SUBOPEN_TT:  ai=0; break;
+        case SUBCLOSE_TT: ai=1; break;
+        case ENDEXP_TT:   ai=2; break;
+        case OP_TT:
+            if (operators[a->value].usage == PRE_U)
+                ai=3;
+            else if (operators[a->value].usage == POST_U)
+                ai=5;
+            else
+                ai=4;
+            break;
+        default:          ai=6; break;
     }
     }
-    switch(b.type)
-    {   case SUBOPEN_TT:  i+=0; break;
-        case SUBCLOSE_TT: i+=5; break;
-        case ENDEXP_TT:   i+=10; break;
-        case OP_TT:       i+=15; break;
-        default:          i+=20; break;
+    switch(b->type)
+    {   case SUBOPEN_TT:  bi=0; break;
+        case SUBCLOSE_TT: bi=1; break;
+        case ENDEXP_TT:   bi=2; break;
+        case OP_TT:
+            if (operators[b->value].usage == PRE_U)
+                bi=3;
+            else if (operators[b->value].usage == POST_U)
+                bi=5;
+            else
+                bi=4;
+            break;
+        default:          bi=6; break;
     }
     }
+    
+    j = prec_table[ai+7*bi];
+    if (j != BYPREC) return j;
 
 
-    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;
+    /* BYPREC is the (a=OP, b=OP) cases. We must compare the precedence of the
+       two operators.
+       (We've already eliminated invalid cases like (a++ --b).)
+    */
+    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:
 
     /*  Anomalous rule to resolve the function call precedence, which is
         different on the right from on the left, e.g., in:
@@ -551,17 +579,18 @@ static int find_prec(token_data a, token_data b)
 
     if (l1 < l2)  return LOWER_P;
     if (l1 > l2)  return GREATER_P;
 
     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 L_A: return GREATER_P;
         case R_A: return LOWER_P;
-        case 0:   return e5;
+        case 0:   return ASSOC_E;
     }
     return GREATER_P;
 }
 
 /* --- Converting token to operand ----------------------------------------- */
 
     }
     return GREATER_P;
 }
 
 /* --- 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,
 int z_system_constant_list[] =
     { adjectives_table_SC,
       actions_table_SC,
@@ -605,10 +634,36 @@ int z_system_constant_list[] =
       highest_class_number_SC,
       class_objects_array_SC,
       highest_object_number_SC,
       highest_class_number_SC,
       class_objects_array_SC,
       highest_object_number_SC,
+      dictionary_table_SC,
+      grammar_table_SC,
       -1 };
 
       -1 };
 
+static void check_system_constant_available(int t)
+{
+    if (OMIT_SYMBOL_TABLE) {
+        /* Certain system constants refer to the symbol table, which
+           is meaningless if OMIT_SYMBOL_TABLE is set. */
+        switch(t)
+        {
+            case identifiers_table_SC:
+            case attribute_names_array_SC:
+            case property_names_array_SC:
+            case action_names_array_SC:
+            case fake_action_names_array_SC:
+            case array_names_offset_SC:
+            case global_names_array_SC:
+            case routine_names_array_SC:
+            case constant_names_array_SC:
+                error_named("OMIT_SYMBOL_TABLE omits system constant", system_constants.keywords[t]);
+            default:
+                break;
+        }
+    }
+}
+
 static int32 value_of_system_constant_z(int t)
 static int32 value_of_system_constant_z(int t)
-{   switch(t)
+{
+    switch(t)
     {   case adjectives_table_SC:
             return adjectives_offset;
         case actions_table_SC:
     {   case adjectives_table_SC:
             return adjectives_offset;
         case actions_table_SC:
@@ -642,9 +697,13 @@ static int32 value_of_system_constant_z(int t)
         case ipv__end_SC:
             return variables_offset;
         case array__start_SC:
         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 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;
 
         case highest_attribute_number_SC:
             return no_attributes-1;
@@ -706,7 +765,8 @@ static int32 value_of_system_constant_z(int t)
     return(0);
 }
 
     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,
 int glulx_system_constant_list[] =
     { classes_table_SC,
       identifiers_table_SC,
@@ -765,7 +825,15 @@ extern int32 value_of_system_constant(int t)
     return value_of_system_constant_g(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.
 {
     /*  If the given token is a constant, evaluate it into the operand.
         For now, the identifiers are considered variables.
@@ -774,13 +842,12 @@ static int evaluate_term(token_data t, assembly_operand *o)
 
     int32 v;
 
 
     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:
     {   case LARGE_NUMBER_TT:
-             v = t.value;
+             v = t->value;
              if (!glulx_mode) {
                  if (v < 0) v = v + 0x10000;
                  o->type = LONG_CONSTANT_OT;
              if (!glulx_mode) {
                  if (v < 0) v = v + 0x10000;
                  o->type = LONG_CONSTANT_OT;
@@ -792,7 +859,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
              }
              return(TRUE);
         case SMALL_NUMBER_TT:
              }
              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;
              if (!glulx_mode) {
                  if (v < 0) v = v + 0x10000;
                  o->type = SHORT_CONSTANT_OT;
@@ -809,7 +876,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  o->type = LONG_CONSTANT_OT;
              else
                  o->type = CONSTANT_OT;
                  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  */
              return(TRUE);
         case DQ_TT:
              /*  Create as a static string  */
@@ -817,14 +884,14 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  o->type = LONG_CONSTANT_OT;
              else
                  o->type = CONSTANT_OT;
                  o->type = LONG_CONSTANT_OT;
              else
                  o->type = CONSTANT_OT;
-             o->value = compile_string(t.text, FALSE, FALSE);
+             o->value = compile_string(t->text, STRCTX_GAME);
              return(TRUE);
         case VARIABLE_TT:
              if (!glulx_mode) {
                  o->type = VARIABLE_OT;
              }
              else {
              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 {
                      o->type = GLOBALVAR_OT;
                  }
                  else {
@@ -833,21 +900,21 @@ static int evaluate_term(token_data t, assembly_operand *o)
                      o->type = LOCALVAR_OT;
                  }
              }
                      o->type = LOCALVAR_OT;
                  }
              }
-             o->value = t.value;
+             o->value = t->value;
              return(TRUE);
         case SYSFUN_TT:
              if (!glulx_mode) {
                  o->type = VARIABLE_OT;
              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;
              }
              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:
              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
              return(TRUE);
         case SYSTEM_CONSTANT_TT:
              /*  Certain system constants depend only on the
@@ -856,7 +923,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  them immediately.  */
              if (!glulx_mode) {
                  o->type = LONG_CONSTANT_OT;
                  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;
                  {   
                  case version_number_SC:
                      o->type = SHORT_CONSTANT_OT;
@@ -873,6 +940,8 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  case dict_par3_SC:
                      o->type = SHORT_CONSTANT_OT;
                      o->marker = 0;
                  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:
                      v = (version_number==3)?6:8; break;
                  case lowest_attribute_number_SC:
                  case lowest_action_number_SC:
@@ -893,7 +962,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:
                      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;
                  }
                      o->marker = INCON_MV;
                      break;
                  }
@@ -901,7 +970,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
              }
              else {
                  o->type = CONSTANT_OT;
              }
              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
                  {
                  /* The three dict_par flags point at the lower byte
                     of the flag field, because the library is written
@@ -943,7 +1012,7 @@ static int evaluate_term(token_data t, assembly_operand *o)
                  /* ###fix: need to fill more of these in! */
 
                  default:
                  /* ###fix: need to fill more of these in! */
 
                  default:
-                     v = t.value;
+                     v = t->value;
                      o->marker = INCON_MV;
                      break;
                  }
                      o->marker = INCON_MV;
                      break;
                  }
@@ -957,35 +1026,40 @@ static int evaluate_term(token_data t, assembly_operand *o)
 
 /* --- Emitter ------------------------------------------------------------- */
 
 
 /* --- 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 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
 
 
 #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 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 ");
 {   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)
         {
         printf(") as ");
         switch(marker)
         {
@@ -1004,29 +1078,28 @@ static void mark_top_of_emitter_stack(int marker, token_data t)
         }
         printf("\n");
     }
         }
         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;
         }
     {   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_sp++;
     }
-    emitter_markers[emitter_sp-1] = marker;
+    emitter_stack[emitter_sp-1].marker = marker;
 }
 
 static void add_bracket_layer_to_emitter_stack(int depth)
 {   /* There's no point in tracking bracket layers that don't fence off any values. */
     if (emitter_sp < depth + 1) return;
     if (expr_trace_level >= 2)
 }
 
 static void add_bracket_layer_to_emitter_stack(int depth)
 {   /* There's no point in tracking bracket layers that don't fence off any values. */
     if (emitter_sp < depth + 1) return;
     if (expr_trace_level >= 2)
-        printf("Adding bracket layer\n");
-    ++emitter_bracket_counts[emitter_sp-depth-1];
+        printf("Adding bracket layer (depth %d)\n", depth);
+    ++emitter_stack[emitter_sp-depth-1].bracket_count;
 }
 
 static void remove_bracket_layer_from_emitter_stack()
 }
 
 static void remove_bracket_layer_from_emitter_stack()
@@ -1034,46 +1107,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_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;
     }
     {   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)
 {   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++)
         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");
     }
 
         }
         printf("\n");
     }
 
-    if (t.type == SUBOPEN_TT) return;
+    if (t->type == SUBOPEN_TT) return;
 
     stack_size = 0;
     while ((stack_size < emitter_sp) &&
 
     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++;
 
         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 ')'");
         {   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");
             }
             else if (stack_size < 1)
                 compiler_error("SR error: emitter stack empty in subexpression");
@@ -1084,14 +1158,14 @@ static void emit_token(token_data t)
         return;
     }
 
         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;
     }
 
         return;
     }
 
@@ -1099,85 +1173,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. */
        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 &&
         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 (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;
         return;
 
     arity = 1;
-    if (t.value == FCALL_OP)
+    if (t->value == FCALL_OP)
     {   if (expr_trace_level >= 3)
         {   printf("FCALL_OP finds marker stack: ");
     {   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");
         }
             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");
             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 &&
         {
             if ((glulx_mode &&
-                 emitter_stack[emitter_sp-arity].type == SYSFUN_OT) ||
+                 emitter_stack[emitter_sp-arity].op.type == SYSFUN_OT) ||
                 (!glulx_mode &&
                 (!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");
                 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;
             }
             ++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;
         {   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;
                 }
                 {   ++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)
                     ++stack_size;
             }
         }
 
         if (arity > stack_size)
-        {   error_named("Missing operand for", t.text);
+        {   error_named("Missing operand for", t->text);
             while (arity > stack_size)
             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++;
             }
         }
     }
 
                 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++)
     {
     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: 
             {
                 case FCALL_OP:
                 case SETEQUALS_OP: case NOTEQUAL_OP: 
@@ -1189,19 +1263,28 @@ static void emit_token(token_data t)
                 case PROPERTY_OP:
                     if (i < arity) break;
                 case GE_OP: case LE_OP:
                 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");
             }
                 default:
                     warning("Property name in expression is not qualified by object");
             }
-        } /* if (is_property_t */
+        }
     }
 
     switch(arity)
     {   case 1:
     }
 
     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))
             if ((o1.marker == 0) && is_constant_ot(o1.type))
-            {   switch(t.value)
-                {   case UNARY_MINUS_OP: x = -o1.value; goto FoldConstant;
+            {   switch(t->value)
+                {   case UNARY_MINUS_OP:
+                        if ((uint32)o1.value == 0x80000000)
+                          x = 0x80000000;
+                        else
+                          x = -o1.value;
+                        goto FoldConstant;
                     case ARTNOT_OP: 
                          if (!glulx_mode)
                              x = (~o1.value) & 0xffff;
                     case ARTNOT_OP: 
                          if (!glulx_mode)
                              x = (~o1.value) & 0xffff;
@@ -1216,8 +1299,8 @@ static void emit_token(token_data t)
             break;
 
         case 2:
             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))
 
             if ((o1.marker == 0) && (o2.marker == 0)
                 && is_constant_ot(o1.type) && is_constant_ot(o2.type))
@@ -1232,7 +1315,7 @@ static void emit_token(token_data t)
                   ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value;
                 }
 
                   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;
                 {
                     case PLUS_OP: x = ov1 + ov2; goto FoldConstantC;
                     case MINUS_OP: x = ov1 - ov2; goto FoldConstantC;
@@ -1242,7 +1325,7 @@ static void emit_token(token_data t)
                         if (ov2 == 0)
                           error("Division of constant by zero");
                         else
                         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;
                           if (ov2 < 0) {
                             ov1 = -ov1;
                             ov2 = -ov2;
@@ -1291,13 +1374,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++;
     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;
     ET[op_node_number].up = -1;
     ET[op_node_number].down = -1;
     ET[op_node_number].right = -1;
@@ -1311,14 +1413,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 (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
         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].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;
         }
         ET[operand_node_number].up = op_node_number;
         ET[operand_node_number].right = -1;
@@ -1333,11 +1435,11 @@ static void emit_token(token_data t)
 
     emitter_sp = emitter_sp - arity + 1;
 
 
     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_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();
 
     /* Remove the marker for the brackets implied by operator precedence */
     remove_bracket_layer_from_emitter_stack();
 
@@ -1349,23 +1451,24 @@ static void emit_token(token_data t)
        for 32-bit arithmetic. */
 
     if (!glulx_mode && ((x<-32768) || (x > 32767)))
        for 32-bit arithmetic. */
 
     if (!glulx_mode && ((x<-32768) || (x > 32767)))
-    {   char folding_error[40];
+    {
         int32 ov1 = (o1.value >= 0x8000) ? (o1.value - 0x10000) : o1.value;
         int32 ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value;
         int32 ov1 = (o1.value >= 0x8000) ? (o1.value - 0x10000) : o1.value;
         int32 ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value;
-        switch(t.value)
+        char op = '?';
+        switch(t->value)
         {
             case PLUS_OP:
         {
             case PLUS_OP:
-                sprintf(folding_error, "%d + %d = %d", ov1, ov2, x);
+                op = '+';
                 break;
             case MINUS_OP:
                 break;
             case MINUS_OP:
-                sprintf(folding_error, "%d - %d = %d", ov1, ov2, x);
+                op = '-';
                 break;
             case TIMES_OP:
                 break;
             case TIMES_OP:
-                sprintf(folding_error, "%d * %d = %d", ov1, ov2, x);
+                op = '*';
                 break;
         }
                 break;
         }
-        error_named("Signed arithmetic on compile-time constants overflowed \
-the range -32768 to +32767:", folding_error);
+        error_fmt("Signed arithmetic on compile-time constants overflowed \
+the range -32768 to +32767 (%d %c %d = %d)", ov1, op, ov2, x);
     }
 
     FoldConstant:
     }
 
     FoldConstant:
@@ -1382,28 +1485,28 @@ the range -32768 to +32767:", folding_error);
 
     if (!glulx_mode) {
         if (x<256)
 
     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)
     }
     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) 
         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) 
         else if (x >= -32768 && x <= 32767) 
-            emitter_stack[emitter_sp - 1].type = HALFCONSTANT_OT;
+            emitter_stack[emitter_sp - 1].op.type = HALFCONSTANT_OT;
         else
         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_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: ");
 
     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");
     }
 
         printf("\n");
     }
 
@@ -1419,9 +1522,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)
     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
         printf("\n");
     }
     else
@@ -1440,12 +1541,10 @@ static void show_node(int n, int depth, int annotate)
     if (ET[n].right != -1) show_node(ET[n].right, depth, annotate);
 }
 
     if (ET[n].right != -1) show_node(ET[n].right, depth, annotate);
 }
 
-extern void show_tree(assembly_operand AO, int annotate)
-{   if (AO.type == EXPRESSION_OT) show_node(AO.value, 0, annotate);
+extern void show_tree(const assembly_operand *AO, int annotate)
+{   if (AO->type == EXPRESSION_OT) show_node(AO->value, 0, annotate);
     else
     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");
     }
 }
         printf("\n");
     }
 }
@@ -1465,11 +1564,14 @@ static void check_property_operator(int from_node)
     if ((below != -1) && (ET[below].right != -1))
     {   int n = ET[below].right, flag = FALSE;
 
     if ((below != -1) && (ET[below].right != -1))
     {   int n = ET[below].right, flag = FALSE;
 
+        /* Can we handle this dot operator as a native @get_prop (etc)
+           opcode? Only if we recognize the property value as a declared
+           common property constant. */
         if ((ET[n].down == -1)
                 && ((ET[n].value.type == LONG_CONSTANT_OT)
                     || (ET[n].value.type == SHORT_CONSTANT_OT))
                 && ((ET[n].value.value > 0) && (ET[n].value.value < 64))
         if ((ET[n].down == -1)
                 && ((ET[n].value.type == LONG_CONSTANT_OT)
                     || (ET[n].value.type == SHORT_CONSTANT_OT))
                 && ((ET[n].value.value > 0) && (ET[n].value.value < 64))
-                && ((!module_switch) || (ET[n].value.marker == 0)))
+                && (ET[n].value.marker == 0))
             flag = TRUE;
 
         if (!flag)
             flag = TRUE;
 
         if (!flag)
@@ -1620,10 +1722,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
 
         (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;
 
 
     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);
     if (ET[n].right != -1) delete_negations(ET[n].right, context);
     if (ET[n].down == -1) return;
     delete_negations(ET[n].down, context);
@@ -1650,9 +1767,9 @@ static void insert_exp_to_cond(int n, int context)
 
     if (ET[n].down == -1)
     {   if (context==CONDITION_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;
             ET[new] = ET[n];
             ET[n].down = new; ET[n].operator_number = NONZERO_OP;
             ET[new].up = n; ET[new].right = -1;
@@ -1673,9 +1790,8 @@ static void insert_exp_to_cond(int n, int context)
         default:
             if (context != CONDITION_CONTEXT) break;
 
         default:
             if (context != CONDITION_CONTEXT) break;
 
+            ensure_memory_list_available(&ET_memlist, ET_used+1);
             new = ET_used++;
             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;
             ET[new] = ET[n];
             ET[n].down = new; ET[n].operator_number = NONZERO_OP;
             ET[new].up = n; ET[new].right = -1;
@@ -1732,9 +1848,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)) {
               || 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++;
           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;
           ET[new] = ET[n];
           ET[n].down = new; 
           ET[n].operator_number = PUSH_OP;
@@ -1755,9 +1870,8 @@ static assembly_operand check_conditions(assembly_operand AO, int context)
 
     if (AO.type != EXPRESSION_OT)
     {   if (context != CONDITION_CONTEXT) return AO;
 
     if (AO.type != EXPRESSION_OT)
     {   if (context != CONDITION_CONTEXT) return AO;
+        ensure_memory_list_available(&ET_memlist, ET_used+1);
         n = ET_used++;
         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;
         ET[n].down = -1;
         ET[n].up = -1;
         ET[n].right = -1;
@@ -1778,7 +1892,8 @@ static assembly_operand check_conditions(assembly_operand AO, int context)
 /* --- Shift-reduce parser ------------------------------------------------- */
 
 static int sr_sp;
 /* --- 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)
 {
 
 extern assembly_operand parse_expression(int context)
 {
@@ -1829,8 +1944,11 @@ extern assembly_operand parse_expression(int context)
         is constant and thus known at compile time.
 
         If an error has occurred in the expression, which recovery from was
         is constant and thus known at compile time.
 
         If an error has occurred in the expression, which recovery from was
-        not possible, then the return is (short constant) 0.  This should
-        minimise the chance of a cascade of further error messages.
+        not possible, then the return is (short constant) 0 with marker
+        value ERROR_MV.  The caller may check for this marker value to
+        decide whether to (e.g.) stop reading array values. Otherwise, it
+        will just be treated as a zero, which should minimise the chance
+        of a cascade of further error messages.
     */
 
     token_data a, b, pop; int i;
     */
 
     token_data a, b, pop; int i;
@@ -1862,6 +1980,7 @@ extern assembly_operand parse_expression(int context)
     previous_token.type = ENDEXP_TT;
     previous_token.value = 0;
 
     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;
 
     sr_sp = 1;
     sr_stack[0] = previous_token;
 
@@ -1871,7 +1990,8 @@ extern assembly_operand parse_expression(int context)
     directives.enabled = FALSE;
 
     if (get_next_etoken() == FALSE)
     directives.enabled = FALSE;
 
     if (get_next_etoken() == FALSE)
-    {   ebf_error("expression", token_text);
+    {   ebf_curtoken_error("expression");
+        AO.marker = ERROR_MV;
         return AO;
     }
 
         return AO;
     }
 
@@ -1885,6 +2005,7 @@ extern assembly_operand parse_expression(int context)
 
         if (sr_sp == 0)
         {   compiler_error("SR error: stack empty");
 
         if (sr_sp == 0)
         {   compiler_error("SR error: stack empty");
+            AO.marker = ERROR_MV;
             return(AO);
         }
 
             return(AO);
         }
 
@@ -1894,18 +2015,20 @@ extern assembly_operand parse_expression(int context)
         {   if (emitter_sp == 0)
             {   error("No expression between brackets '(' and ')'");
                 put_token_back();
         {   if (emitter_sp == 0)
             {   error("No expression between brackets '(' and ')'");
                 put_token_back();
+                AO.marker = ERROR_MV;
                 return AO;
             }
             if (emitter_sp > 1)
             {   compiler_error("SR error: emitter stack overfull");
                 return AO;
             }
             if (emitter_sp > 1)
             {   compiler_error("SR error: emitter stack overfull");
+                AO.marker = ERROR_MV;
                 return AO;
             }
 
                 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");
             if (AO.type == EXPRESSION_OT)
             {   if (expr_trace_level >= 3)
                 {   printf("Tree before lvalue checking:\n");
-                    show_tree(AO, FALSE);
+                    show_tree(&AO, FALSE);
                 }
                 if (!glulx_mode)
                     check_property_operator(AO.value);
                 }
                 if (!glulx_mode)
                     check_property_operator(AO.value);
@@ -1913,7 +2036,9 @@ extern assembly_operand parse_expression(int context)
                 ET[AO.value].up = -1;
             }
             else {
                 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?");
             }
                     && (arrow_allowed) && (!bare_prop_allowed))
                     warning("Bare property name found. \"self.prop\" intended?");
             }
@@ -1923,6 +2048,7 @@ extern assembly_operand parse_expression(int context)
             if (context == CONSTANT_CONTEXT)
                 if (!is_constant_ot(AO.type))
                 {   AO = zero_operand;
             if (context == CONSTANT_CONTEXT)
                 if (!is_constant_ot(AO.type))
                 {   AO = zero_operand;
+                    AO.marker = ERROR_MV;
                     ebf_error("constant", "<expression>");
                 }
             put_token_back();
                     ebf_error("constant", "<expression>");
                 }
             put_token_back();
@@ -1930,22 +2056,21 @@ extern assembly_operand parse_expression(int context)
             return(AO);
         }
 
             return(AO);
         }
 
-        switch(find_prec(a,b))
+        switch(find_prec(&a,&b))
         {
         {
-            case e5:                 /* Associativity error                  */
+            case ASSOC_E:            /* Associativity error                  */
                 error_named("Brackets mandatory to clarify order of:",
                     a.text);
 
             case LOWER_P:
             case EQUAL_P:
                 error_named("Brackets mandatory to clarify order of:",
                     a.text);
 
             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)
                 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;
                         else
                             add_bracket_layer_to_emitter_stack(0);
                         break;
@@ -1954,7 +2079,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)
                             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 */
                                 else
                                 {   error("'or' not between values to the right of a condition");
                                     /* Convert to + for error recovery purposes */
@@ -1970,7 +2095,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)
                                     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. */
                                         break;
                                     }
                                     /* Non-argument-separating commas get treated like any other operator; we fall through to the default case. */
@@ -1988,13 +2113,15 @@ extern assembly_operand parse_expression(int context)
             case GREATER_P:
                 do
                 {   pop = sr_stack[sr_sp - 1];
             case GREATER_P:
                 do
                 {   pop = sr_stack[sr_sp - 1];
-                    emit_token(pop);
+                    emit_token(&pop);
                     sr_sp--;
                     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;
 
                 break;
 
-            case e1:                 /* Missing operand error                */
+            case NOVAL_E:            /* Missing operand error                */
                 error_named("Missing operand after", a.text);
                 error_named("Missing operand after", a.text);
+                /* We insert a "0" token so that the rest of the expression
+                   can be compiled. */
                 put_token_back();
                 current_token.type = NUMBER_TT;
                 current_token.value = 0;
                 put_token_back();
                 current_token.type = NUMBER_TT;
                 current_token.value = 0;
@@ -2002,13 +2129,15 @@ extern assembly_operand parse_expression(int context)
                 current_token.text = "0";
                 break;
 
                 current_token.text = "0";
                 break;
 
-            case e2:                 /* Unexpected close bracket             */
+            case CLOSEB_E:           /* Unexpected close bracket             */
                 error("Found '(' without matching ')'");
                 get_next_etoken();
                 break;
 
                 error("Found '(' without matching ')'");
                 get_next_etoken();
                 break;
 
-            case e3:                 /* Missing operator error               */
-                error("Missing operator: inserting '+'");
+            case NOOP_E:             /* Missing operator error               */
+                error_named("Missing operator after", a.text);
+                /* We insert a "+" token so that the rest of the expression
+                   can be compiled. */
                 put_token_back();
                 current_token.type = OP_TT;
                 current_token.value = PLUS_OP;
                 put_token_back();
                 current_token.type = OP_TT;
                 current_token.value = PLUS_OP;
@@ -2016,7 +2145,7 @@ extern assembly_operand parse_expression(int context)
                 current_token.text = "+";
                 break;
 
                 current_token.text = "+";
                 break;
 
-            case e4:                 /* Expression ends with an open bracket */
+            case OPENB_E:            /* Expression ends with an open bracket */
                 error("Found '(' without matching ')'");
                 sr_sp--;
                 break;
                 error("Found '(' without matching ')'");
                 sr_sp--;
                 break;
@@ -2044,6 +2173,80 @@ extern int test_for_incdec(assembly_operand AO)
     return s*(ET[ET[AO.value].down].value.value);
 }
 
     return s*(ET[ET[AO.value].down].value.value);
 }
 
+
+/* Determine if the operand (a parsed expression) is a constant (as
+   per is_constant_ot()) or a comma-separated list of such constants.
+   
+   "(1)" and "(1,2,3)" both count, and even "((1,2),3)", but
+   not "(1,(2,3))"; the list must be left-associated.
+
+   Backpatched constants (function names, etc) are acceptable, as are
+   folded constant expressions. Variables are right out.
+
+   The constants are stored in the ops_found array, up to a maximum of
+   max_ops_found. For Inform parsing reasons, the array list is backwards
+   from the order found.
+
+   Returns the number of constants found. If the expression is not a list of
+   constants, returns zero.
+   
+   (The return value may be more than max_ops_found, in which case we weren't
+   able to return them all in the array.)
+*/
+extern int test_constant_op_list(const assembly_operand *AO, assembly_operand *ops_found, int max_ops_found)
+{
+    int count = 0;
+    int n;
+
+    if (AO->type != EXPRESSION_OT) {
+        if (!is_constant_ot(AO->type))
+            return 0;
+
+        if (ops_found && max_ops_found > 0)
+            ops_found[0] = *AO;
+        return 1;
+    }
+
+    n = AO->value;
+
+    /* For some reason the top node is always a COMMA with no .right,
+       just a .down. Should we rely on this? For now yes. */
+
+    if (operators[ET[n].operator_number].token_value != COMMA_SEP)
+        return 0;
+    if (ET[n].right != -1)
+        return 0;
+    n = ET[n].down;
+
+    while (TRUE) {
+        if (ET[n].right != -1) {
+            if (ET[ET[n].right].down != -1)
+                return 0;
+            if (!is_constant_ot(ET[ET[n].right].value.type))
+                return 0;
+            
+            if (ops_found && max_ops_found > count)
+                ops_found[count] = ET[ET[n].right].value;
+            count++;
+        }
+
+        if (ET[n].down == -1) {
+            if (!is_constant_ot(ET[n].value.type))
+                return 0;
+            
+            if (ops_found && max_ops_found > count)
+                ops_found[count] = ET[n].value;
+            count++;
+            return count;
+        }
+        
+        if (operators[ET[n].operator_number].token_value != COMMA_SEP)
+            return 0;
+
+        n = ET[n].down;
+    }
+}
+
 /* ========================================================================= */
 /*   Data structure management routines                                      */
 /* ------------------------------------------------------------------------- */
 /* ========================================================================= */
 /*   Data structure management routines                                      */
 /* ------------------------------------------------------------------------- */
@@ -2052,7 +2255,12 @@ extern void init_expressp_vars(void)
 {   int i;
     /* make_operands(); */
     make_lexical_interface_tables();
 {   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)
 }
 
 extern void expressp_begin_pass(void)
@@ -2060,24 +2268,27 @@ extern void expressp_begin_pass(void)
 }
 
 extern void expressp_allocate_arrays(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");
         "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)
         "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);
 }
 
 /* ========================================================================= */
 }
 
 /* ========================================================================= */