Update to Inform v6.42
[inform.git] / src / states.c
index 3f0e8740a09a85b90266f8d603a2e1a74fc816e4..b0695e956c50ee1cfc9ea7900d6c45732382356d 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "states" :  Statement translator                                        */
 /*                                                                           */
-/*   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      */
@@ -15,7 +15,7 @@
 /* GNU General Public License for more details.                              */
 /*                                                                           */
 /* You should have received a copy of the GNU General Public License         */
-/* along with Inform. If not, see https://gnu.org/licenses/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -29,13 +29,13 @@ static int match_colon(void)
 of a 'for' loop specification: replacing ';' with ':'");
         else
         if (token_value != COLON_SEP)
-        {   ebf_error("':'", token_text);
+        {   ebf_curtoken_error("':'");
             panic_mode_error_recovery();
             return(FALSE);
         }
     }
     else
-    {   ebf_error("':'", token_text);
+    {   ebf_curtoken_error("':'");
         panic_mode_error_recovery();
         return(FALSE);
     }
@@ -46,14 +46,14 @@ static void match_open_bracket(void)
 {   get_next_token();
     if ((token_type == SEP_TT) && (token_value == OPENB_SEP)) return;
     put_token_back();
-    ebf_error("'('", token_text);
+    ebf_curtoken_error("'('");
 }
 
 extern void match_close_bracket(void)
 {   get_next_token();
     if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP)) return;
     put_token_back();
-    ebf_error("')'", token_text);
+    ebf_curtoken_error("')'");
 }
 
 static void parse_action(void)
@@ -98,7 +98,11 @@ static void parse_action(void)
         codegen_action = TRUE;
     }
     else
-    {   codegen_action = FALSE;
+    {
+        if (token_type != UQ_TT) {
+            ebf_curtoken_error("name of action");
+        }
+        codegen_action = FALSE;
         AO2 = action_of_name(token_text);
     }
 
@@ -121,7 +125,7 @@ static void parse_action(void)
     }
     if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
     {
-        ebf_error("',' or '>'", token_text);
+        ebf_curtoken_error("',' or '>'");
     }
 
     if ((token_type == SEP_TT) && (token_value == COMMA_SEP))
@@ -135,7 +139,7 @@ static void parse_action(void)
         get_next_token();
         if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
         {
-            ebf_error("'>'", token_text);
+            ebf_curtoken_error("'>'");
         }
     }
 
@@ -143,7 +147,7 @@ static void parse_action(void)
     {   get_next_token();
         if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
         {   put_token_back();
-            ebf_error("'>>'", token_text);
+            ebf_curtoken_error("'>>'");
         }
     }
 
@@ -259,20 +263,20 @@ extern int parse_label(void)
     get_next_token();
 
     if ((token_type == SYMBOL_TT) &&
-        (stypes[token_value] == LABEL_T))
-    {   sflags[token_value] |= USED_SFLAG;
-        return(svals[token_value]);
+        (symbols[token_value].type == LABEL_T))
+    {   symbols[token_value].flags |= USED_SFLAG;
+        return(symbols[token_value].value);
     }
 
-    if ((token_type == SYMBOL_TT) && (sflags[token_value] & UNKNOWN_SFLAG))
+    if ((token_type == SYMBOL_TT) && (symbols[token_value].flags & UNKNOWN_SFLAG))
     {   assign_symbol(token_value, next_label, LABEL_T);
         define_symbol_label(token_value);
         next_label++;
-        sflags[token_value] |= CHANGE_SFLAG + USED_SFLAG;
-        return(svals[token_value]);
+        symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG;
+        return(symbols[token_value].value);
     }
 
-    ebf_error("label name", token_text);
+    ebf_curtoken_error("label name");
     return 0;
 }
 
@@ -305,10 +309,15 @@ static void parse_print_z(int finally_return)
         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
         switch(token_type)
         {   case DQ_TT:
-              if (strlen(token_text) > 32)
+              if (token_text[0] == '^' && token_text[1] == '\0') {
+                  /* The string "^" is always a simple newline. */
+                  assemblez_0(new_line_zc);
+                  break;
+              }
+              if ((int)strlen(token_text) > ZCODE_MAX_INLINE_STRING)
               {   INITAOT(&AO, LONG_CONSTANT_OT);
                   AO.marker = STRING_MV;
-                  AO.value  = compile_string(token_text, FALSE, FALSE);
+                  AO.value  = compile_string(token_text, STRCTX_GAME);
                   assemblez_1(print_paddr_zc, AO);
                   if (finally_return)
                   {   get_next_token();
@@ -429,19 +438,21 @@ static void parse_print_z(int finally_return)
                           break;
 
                         case SYMBOL_TT:
-                          if (sflags[token_value] & UNKNOWN_SFLAG)
+                          if (symbols[token_value].flags & UNKNOWN_SFLAG)
                           {   INITAOT(&AO, LONG_CONSTANT_OT);
                               AO.value = token_value;
                               AO.marker = SYMBOL_MV;
+                              AO.symindex = token_value;
                           }
                           else
                           {   INITAOT(&AO, LONG_CONSTANT_OT);
-                              AO.value = svals[token_value];
+                              AO.value = symbols[token_value].value;
                               AO.marker = IROUTINE_MV;
-                              if (stypes[token_value] != ROUTINE_T)
-                                ebf_error("printing routine name", token_text);
+                              AO.symindex = token_value;
+                              if (symbols[token_value].type != ROUTINE_T)
+                                ebf_curtoken_error("printing routine name");
                           }
-                          sflags[token_value] |= USED_SFLAG;
+                          symbols[token_value].flags |= USED_SFLAG;
 
                           PrintByRoutine:
 
@@ -460,7 +471,7 @@ static void parse_print_z(int finally_return)
                                 QUANTITY_CONTEXT, -1), temp_var1);
                           goto PrintTermDone;
 
-                        default: ebf_error("print specification", token_text);
+                        default: ebf_curtoken_error("print specification");
                           get_next_token();
                           assemblez_1(print_num_zc,
                           code_generate(parse_expression(QUANTITY_CONTEXT),
@@ -490,13 +501,13 @@ static void parse_print_z(int finally_return)
         get_next_token();
         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
         if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
-        {   ebf_error("comma", token_text);
+        {   ebf_curtoken_error("comma");
             panic_mode_error_recovery(); return;
         }
         else get_next_token();
     } while(TRUE);
 
-    if (count == 0) ebf_error("something to print", token_text);
+    if (count == 0) ebf_curtoken_error("something to print");
     if (finally_return)
     {   assemblez_0(new_line_zc);
         assemblez_0(rtrue_zc);
@@ -533,11 +544,17 @@ static void parse_print_g(int finally_return)
         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
         switch(token_type)
         {   case DQ_TT:
+              if (token_text[0] == '^' && token_text[1] == '\0') {
+                  /* The string "^" is always a simple newline. */
+                  INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
+                  assembleg_1(streamchar_gc, AO);
+                  break;
+              }
               /* We can't compile a string into the instruction,
                  so this always goes into the string area. */
               {   INITAOT(&AO, CONSTANT_OT);
                   AO.marker = STRING_MV;
-                  AO.value  = compile_string(token_text, FALSE, FALSE);
+                  AO.value  = compile_string(token_text, STRCTX_GAME);
                   assembleg_1(streamstr_gc, AO);
                   if (finally_return)
                   {   get_next_token();
@@ -562,7 +579,6 @@ static void parse_print_g(int finally_return)
                   get_next_token();
                   if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
                   {   assembly_operand AO1;
-                      int ln, ln2;
 
                       put_token_back(); put_token_back();
                       local_variables.enabled = FALSE;
@@ -589,19 +605,15 @@ static void parse_print_g(int finally_return)
                                   AO1 = code_generate(
                                       parse_expression(QUANTITY_CONTEXT),
                                       QUANTITY_CONTEXT, -1);
-                                  if ((AO1.type == LOCALVAR_OT) && (AO1.value == 0))
-                                  {   assembleg_2(stkpeek_gc, zero_operand, 
-                                      stack_pointer);
+                                  if (is_constant_ot(AO1.type) && AO1.marker == 0) {
+                                      if (AO1.value >= 0 && AO1.value < 0x100)
+                                          assembleg_1(streamchar_gc, AO1);
+                                      else
+                                          assembleg_1(streamunichar_gc, AO1);
+                                  }
+                                  else {
+                                      assembleg_1(streamunichar_gc, AO1);
                                   }
-                                  INITAOTV(&AO2, HALFCONSTANT_OT, 0x100);
-                                  assembleg_2_branch(jgeu_gc, AO1, AO2, 
-                                      ln = next_label++);
-                                  ln2 = next_label++;
-                                  assembleg_1(streamchar_gc, AO1);
-                                  assembleg_jump(ln2);
-                                  assemble_label_no(ln);
-                                  assembleg_1(streamunichar_gc, AO1);
-                                  assemble_label_no(ln2);
                                   goto PrintTermDone;
                               case ADDRESS_MK:
                                   if (runtime_error_checking_switch)
@@ -664,19 +676,21 @@ static void parse_print_g(int finally_return)
                           break;
 
                         case SYMBOL_TT:
-                          if (sflags[token_value] & UNKNOWN_SFLAG)
+                          if (symbols[token_value].flags & UNKNOWN_SFLAG)
                           {   INITAOT(&AO, CONSTANT_OT);
                               AO.value = token_value;
                               AO.marker = SYMBOL_MV;
+                              AO.symindex = token_value;
                           }
                           else
                           {   INITAOT(&AO, CONSTANT_OT);
-                              AO.value = svals[token_value];
+                              AO.value = symbols[token_value].value;
                               AO.marker = IROUTINE_MV;
-                              if (stypes[token_value] != ROUTINE_T)
-                                ebf_error("printing routine name", token_text);
+                              AO.symindex = token_value;
+                              if (symbols[token_value].type != ROUTINE_T)
+                                ebf_curtoken_error("printing routine name");
                           }
-                          sflags[token_value] |= USED_SFLAG;
+                          symbols[token_value].flags |= USED_SFLAG;
 
                           PrintByRoutine:
 
@@ -688,7 +702,7 @@ static void parse_print_g(int finally_return)
                             AO2);
                           goto PrintTermDone;
 
-                        default: ebf_error("print specification", token_text);
+                        default: ebf_curtoken_error("print specification");
                           get_next_token();
                           assembleg_1(streamnum_gc,
                           code_generate(parse_expression(QUANTITY_CONTEXT),
@@ -718,13 +732,13 @@ static void parse_print_g(int finally_return)
         get_next_token();
         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
         if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
-        {   ebf_error("comma", token_text);
+        {   ebf_curtoken_error("comma");
             panic_mode_error_recovery(); return;
         }
         else get_next_token();
     } while(TRUE);
 
-    if (count == 0) ebf_error("something to print", token_text);
+    if (count == 0) ebf_curtoken_error("something to print");
     if (finally_return)
     {
         INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
@@ -734,57 +748,73 @@ static void parse_print_g(int finally_return)
     }
 }
 
-static void parse_statement_z(int break_label, int continue_label)
-{   int ln, ln2, ln3, ln4, flag;
-    assembly_operand AO, AO2, AO3, AO4;
-    debug_location spare_debug_location1, spare_debug_location2;
-
-    ASSERT_ZCODE();
-
-    if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
+/* Parse any number of ".Label;" lines before a statement.
+   Returns whether a statement can in fact follow. */
+static int parse_named_label_statements()
+{
+    while ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
     {   /*  That is, a full stop, signifying a label  */
 
         get_next_token();
-        if (token_type == SYMBOL_TT)
+        if (token_type != SYMBOL_TT)
         {
-            if (sflags[token_value] & UNKNOWN_SFLAG)
-            {   assign_symbol(token_value, next_label, LABEL_T);
-                sflags[token_value] |= USED_SFLAG;
-                assemble_label_no(next_label);
-                define_symbol_label(token_value);
-                next_label++;
+            ebf_curtoken_error("label name");
+            return TRUE;
+        }
+
+        if (symbols[token_value].flags & UNKNOWN_SFLAG)
+        {   assign_symbol(token_value, next_label, LABEL_T);
+            symbols[token_value].flags |= USED_SFLAG;
+            assemble_label_no(next_label);
+            define_symbol_label(token_value);
+            next_label++;
+        }
+        else
+        {   if (symbols[token_value].type != LABEL_T) {
+                ebf_curtoken_error("label name");
+                return TRUE;
             }
-            else
-            {   if (stypes[token_value] != LABEL_T) goto LabelError;
-                if (sflags[token_value] & CHANGE_SFLAG)
-                {   sflags[token_value] &= (~(CHANGE_SFLAG));
-                    assemble_label_no(svals[token_value]);
-                    define_symbol_label(token_value);
-                }
-                else error_named("Duplicate definition of label:", token_text);
+            if (symbols[token_value].flags & CHANGE_SFLAG)
+            {   symbols[token_value].flags &= (~(CHANGE_SFLAG));
+                assemble_label_no(symbols[token_value].value);
+                define_symbol_label(token_value);
             }
+            else error_named("Duplicate definition of label:", token_text);
+        }
 
-            get_next_token();
-            if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
-            {   ebf_error("';'", token_text);
-                put_token_back(); return;
-            }
+        get_next_token();
+        if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
+        {   ebf_curtoken_error("';'");
+            put_token_back(); return FALSE;
+        }
 
-            /*  Interesting point of Inform grammar: a statement can only
-                consist solely of a label when it is immediately followed
-                by a "}".                                                    */
+        /*  Interesting point of Inform grammar: a statement can only
+            consist solely of a label when it is immediately followed
+            by a "}".                                                    */
 
-            get_next_token();
-            if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
-            {   put_token_back(); return;
-            }
-            statement_debug_location = get_token_location();
-            parse_statement(break_label, continue_label);
-            return;
+        get_next_token();
+        if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
+        {   put_token_back(); return FALSE;
         }
-        LabelError: ebf_error("label name", token_text);
+        /* The following line prevents labels from influencing the positions
+           of sequence points. */
+        statement_debug_location = get_token_location();
+        
+        /* Another label might follow */
     }
 
+    /* On with the statement */
+    return TRUE;
+}
+
+static void parse_statement_z(int break_label, int continue_label)
+{   int ln, ln2, ln3, ln4, flag;
+    int pre_unreach, labelexists;
+    assembly_operand AO, AO2, AO3, AO4;
+    debug_location spare_debug_location1, spare_debug_location2;
+
+    ASSERT_ZCODE();
+
     if ((token_type == SEP_TT) && (token_value == HASH_SEP))
     {   parse_directive(TRUE);
         parse_statement(break_label, continue_label); return;
@@ -804,8 +834,10 @@ static void parse_statement_z(int break_label, int continue_label)
     {   parse_action(); goto StatementTerminator; }
 
     if (token_type == EOF_TT)
-    {   ebf_error("statement", token_text); return; }
+    {   ebf_curtoken_error("statement"); return; }
 
+    /* If we don't see a keyword, this must be a function call or
+       other expression-with-side-effects. */
     if (token_type != STATEMENT_TT)
     {   put_token_back();
         AO = parse_expression(VOID_CONTEXT);
@@ -834,8 +866,7 @@ static void parse_statement_z(int break_label, int continue_label)
                      if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
                          break;
                      if (token_type != DQ_TT)
-                         ebf_error("text of box line in double-quotes",
-                             token_text);
+                         ebf_curtoken_error("text of box line in double-quotes");
                      {   int i, j;
                          for (i=0, j=0; token_text[i] != 0; j++)
                              if (token_text[i] == '@')
@@ -901,7 +932,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  get_next_token();
                  if ((token_type == STATEMENT_TT)
                      && (token_value == UNTIL_CODE))
-                 {   assemble_label_no(ln2);
+                 {   assemble_forward_label_no(ln2);
                      match_open_bracket();
                      AO = parse_expression(CONDITION_CONTEXT);
                      match_close_bracket();
@@ -909,7 +940,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  }
                  else error("'do' without matching 'until'");
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  break;
 
     /*  -------------------------------------------------------------------- */
@@ -923,7 +954,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  if ((token_type != MISC_KEYWORD_TT)
                      || ((token_value != ON_MK)
                          && (token_value != OFF_MK)))
-                 {   ebf_error("'on' or 'off'", token_text);
+                 {   ebf_curtoken_error("'on' or 'off'");
                      panic_mode_error_recovery();
                      break;
                  }
@@ -974,6 +1005,10 @@ static void parse_statement_z(int break_label, int continue_label)
                  get_next_token();
 
                  /*  Initialisation code  */
+                 AO.type = OMITTED_OT;
+                 spare_debug_location1 = statement_debug_location;
+                 AO2.type = OMITTED_OT; flag = 0;
+                 spare_debug_location2 = statement_debug_location;
 
                  if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
                  {   put_token_back();
@@ -993,10 +1028,9 @@ static void parse_statement_z(int break_label, int continue_label)
                              sequence_point_follows = FALSE;
                              if (!execution_never_reaches_here)
                                  assemblez_jump(ln);
-                             assemble_label_no(ln2);
+                             assemble_forward_label_no(ln2);
                              return;
                          }
-                         AO.type = OMITTED_OT;
                          goto ParseUpdate;
                      }
                      put_token_back();
@@ -1004,7 +1038,6 @@ static void parse_statement_z(int break_label, int continue_label)
                  }
 
                  get_next_token();
-                 AO.type = OMITTED_OT;
                  if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
                  {   put_token_back();
                      spare_debug_location1 = get_token_location();
@@ -1014,7 +1047,6 @@ static void parse_statement_z(int break_label, int continue_label)
                  get_next_token();
 
                  ParseUpdate:
-                 AO2.type = OMITTED_OT; flag = 0;
                  if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
                  {   put_token_back();
                      spare_debug_location2 = get_token_location();
@@ -1079,16 +1111,10 @@ static void parse_statement_z(int break_label, int continue_label)
                      statement_debug_location = spare_debug_location2;
                      if (flag > 0)
                      {   INITAOTV(&AO3, SHORT_CONSTANT_OT, flag);
-                         if (module_switch
-                             && (flag>=MAX_LOCAL_VARIABLES) && (flag<LOWEST_SYSTEM_VAR_NUMBER))
-                             AO3.marker = VARIABLE_MV;
                          assemblez_1(inc_zc, AO3);
                      }
                      else
                      {   INITAOTV(&AO3, SHORT_CONSTANT_OT, -flag);
-                         if ((module_switch) && (flag>=MAX_LOCAL_VARIABLES)
-                             && (flag<LOWEST_SYSTEM_VAR_NUMBER))
-                             AO3.marker = VARIABLE_MV;
                          assemblez_1(dec_zc, AO3);
                      }
                      assemblez_jump(ln);
@@ -1105,7 +1131,7 @@ static void parse_statement_z(int break_label, int continue_label)
                      }
                  }
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1115,6 +1141,7 @@ static void parse_statement_z(int break_label, int continue_label)
         case GIVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                           QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
                  if ((AO.type == VARIABLE_OT) && (AO.value == 0))
                  {   INITAOTV(&AO, SHORT_CONSTANT_OT, 252);
                      if (version_number != 6) assemblez_1(pull_zc, AO);
@@ -1129,15 +1156,12 @@ static void parse_statement_z(int break_label, int continue_label)
                      if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
                          ln = clear_attr_zc;
                      else
-                     {   if ((token_type == SYMBOL_TT)
-                             && (stypes[token_value] != ATTRIBUTE_T))
-                           warning_named("This is not a declared Attribute:",
-                             token_text);
-                         ln = set_attr_zc;
+                     {   ln = set_attr_zc;
                          put_token_back();
                      }
                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1);
+                     check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
                      if (runtime_error_checking_switch)
                      {   ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
                          if (version_number >= 5)
@@ -1158,8 +1182,9 @@ static void parse_statement_z(int break_label, int continue_label)
     /*  -------------------------------------------------------------------- */
 
         case IF_CODE:
-                 flag = FALSE;
+                 flag = FALSE; /* set if there's an "else" */
                  ln2 = 0;
+                 pre_unreach = execution_never_reaches_here;
 
                  match_open_bracket();
                  AO = parse_expression(CONDITION_CONTEXT);
@@ -1177,14 +1202,23 @@ static void parse_statement_z(int break_label, int continue_label)
                      ln = next_label++;
                  }
 
+                 /* The condition */
                  code_generate(AO, CONDITION_CONTEXT, ln);
 
+                 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
+                     /* If the condition never falls through to here, then
+                        it was an "if (0)" test. Our convention is to skip
+                        the "not reached" warnings for this case. */
+                     execution_never_reaches_here |= EXECSTATE_NOWARN;
+                 }
+
+                 /* The "if" block */
                  if (ln >= 0) parse_code_block(break_label, continue_label, 0);
                  else
                  {   get_next_token();
                      if ((token_type != SEP_TT)
                          || (token_value != SEMICOLON_SEP))
-                     {   ebf_error("';'", token_text);
+                     {   ebf_curtoken_error("';'");
                          put_token_back();
                      }
                  }
@@ -1211,13 +1245,46 @@ static void parse_statement_z(int break_label, int continue_label)
                  }
                  else put_token_back();
 
-                 if (ln >= 0) assemble_label_no(ln);
+                 /* The "else" label (or end of statement, if there is no "else") */
+                 labelexists = FALSE;
+                 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
 
                  if (flag)
-                 {   parse_code_block(break_label, continue_label, 0);
-                     if (ln >= 0) assemble_label_no(ln2);
-                 }
+                 {
+                     /* If labelexists is false, then we started with
+                        "if (1)". In this case, we don't want a "not
+                        reached" warning on the "else" block. We
+                        temporarily disable the NOWARN flag, and restore it
+                        afterwards. */
+                     int saved_unreach = 0;
+                     if (execution_never_reaches_here && !labelexists) {
+                         saved_unreach = execution_never_reaches_here;
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+
+                     /* The "else" block */
+                     parse_code_block(break_label, continue_label, 0);
 
+                     if (execution_never_reaches_here && !labelexists) {
+                         if (saved_unreach & EXECSTATE_NOWARN)
+                             execution_never_reaches_here |= EXECSTATE_NOWARN;
+                         else
+                             execution_never_reaches_here &= ~EXECSTATE_NOWARN;
+                     }
+
+                     /* The post-"else" label */
+                     if (ln >= 0) assemble_forward_label_no(ln2);
+                 }
+                 else
+                 {
+                     /* There was no "else". If we're unreachable, then the
+                        statement returned unconditionally, which means 
+                        "if (1) return". Skip warnings. */
+                     if (!pre_unreach && execution_never_reaches_here) {
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+                 }
+                         
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1262,7 +1329,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  misc_keywords.enabled = FALSE;
                  if ((token_type != MISC_KEYWORD_TT)
                      || (token_value != TO_MK))
-                 {   ebf_error("'to'", token_text);
+                 {   ebf_curtoken_error("'to'");
                      panic_mode_error_recovery();
                      return;
                  }
@@ -1270,6 +1337,8 @@ static void parse_statement_z(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  AO = code_generate(AO, QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
+                 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                  {   if (version_number >= 5)
                          assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
@@ -1302,15 +1371,12 @@ static void parse_statement_z(int break_label, int continue_label)
                      AO.value = token_value;
                  else
                  if ((token_type == SYMBOL_TT) &&
-                     (stypes[token_value] == GLOBAL_VARIABLE_T))
-                     AO.value = svals[token_value];
+                     (symbols[token_value].type == GLOBAL_VARIABLE_T))
+                     AO.value = symbols[token_value].value;
                  else
-                 {   ebf_error("'objectloop' variable", token_text);
+                 {   ebf_curtoken_error("'objectloop' variable");
                      panic_mode_error_recovery(); break;
                  }
-                 if ((module_switch) && (AO.value >= MAX_LOCAL_VARIABLES)
-                     && (AO.value < LOWEST_SYSTEM_VAR_NUMBER))
-                     AO.marker = VARIABLE_MV;
                  misc_keywords.enabled = TRUE;
                  get_next_token(); flag = TRUE;
                  misc_keywords.enabled = FALSE;
@@ -1489,6 +1555,7 @@ static void parse_statement_z(int break_label, int continue_label)
         case REMOVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                  {   if (version_number >= 5)
                          assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR),
@@ -1598,18 +1665,18 @@ static void parse_statement_z(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  if (is_constant_ot(AO2.type) && AO2.marker == 0) {
-                     if (AO2.value >= 96)
-                     {   error("Z-machine dynamic strings are limited to 96");
+                     /* Compile-time check */
+                     if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
+                         error_max_dynamic_strings(AO2.value);
                          AO2.value = 0;
                      }
-                     if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
-                         memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS);
-                     }
                  }
                  get_next_token();
                  if (token_type == DQ_TT)
                  {   INITAOT(&AO4, LONG_CONSTANT_OT);
-                     AO4.value = compile_string(token_text, TRUE, TRUE);
+                     /* This string must be in low memory so that the
+                        dynamic string table can refer to it. */
+                     AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
                  }
                  else
                  {   put_token_back();
@@ -1639,9 +1706,8 @@ static void parse_statement_z(int break_label, int continue_label)
                          && (token_value != BOLD_MK)
                          && (token_value != UNDERLINE_MK)
                          && (token_value != FIXED_MK)))
-                 {   ebf_error(
-"'roman', 'bold', 'underline', 'reverse' or 'fixed'",
-                         token_text);
+                 {   ebf_curtoken_error(
+"'roman', 'bold', 'underline', 'reverse' or 'fixed'");
                      panic_mode_error_recovery();
                      break;
                  }
@@ -1670,7 +1736,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  assemblez_store(AO2, AO);
 
                  parse_code_block(ln = next_label++, continue_label, 1);
-                 assemble_label_no(ln);
+                 assemble_forward_label_no(ln);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1688,7 +1754,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  parse_code_block(ln2, ln, 0);
                  sequence_point_follows = FALSE;
                  assemblez_jump(ln);
-                 assemble_label_no(ln2);
+                 assemble_forward_label_no(ln2);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1706,64 +1772,19 @@ static void parse_statement_z(int break_label, int continue_label)
 
     get_next_token();
     if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
-    {   ebf_error("';'", token_text);
+    {   ebf_curtoken_error("';'");
         put_token_back();
     }
 }
 
 static void parse_statement_g(int break_label, int continue_label)
 {   int ln, ln2, ln3, ln4, flag, onstack;
+    int pre_unreach, labelexists;
     assembly_operand AO, AO2, AO3, AO4;
     debug_location spare_debug_location1, spare_debug_location2;
 
     ASSERT_GLULX();
 
-    if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
-    {   /*  That is, a full stop, signifying a label  */
-
-        get_next_token();
-        if (token_type == SYMBOL_TT)
-        {
-            if (sflags[token_value] & UNKNOWN_SFLAG)
-            {   assign_symbol(token_value, next_label, LABEL_T);
-                sflags[token_value] |= USED_SFLAG;
-                assemble_label_no(next_label);
-                define_symbol_label(token_value);
-                next_label++;
-            }
-            else
-            {   if (stypes[token_value] != LABEL_T) goto LabelError;
-                if (sflags[token_value] & CHANGE_SFLAG)
-                {   sflags[token_value] &= (~(CHANGE_SFLAG));
-                    assemble_label_no(svals[token_value]);
-                    define_symbol_label(token_value);
-                }
-                else error_named("Duplicate definition of label:", token_text);
-            }
-
-            get_next_token();
-            if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
-            {   ebf_error("';'", token_text);
-                put_token_back(); return;
-            }
-
-            /*  Interesting point of Inform grammar: a statement can only
-                consist solely of a label when it is immediately followed
-                by a "}".                                                    */
-
-            get_next_token();
-            if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
-            {   put_token_back(); return;
-            }
-            /* The following line prevents labels from influencing the positions
-               of sequence points. */
-            statement_debug_location = get_token_location();
-            parse_statement(break_label, continue_label);
-            return;
-        }
-        LabelError: ebf_error("label name", token_text);
-    }
-
     if ((token_type == SEP_TT) && (token_value == HASH_SEP))
     {   parse_directive(TRUE);
         parse_statement(break_label, continue_label); return;
@@ -1783,8 +1804,10 @@ static void parse_statement_g(int break_label, int continue_label)
     {   parse_action(); goto StatementTerminator; }
 
     if (token_type == EOF_TT)
-    {   ebf_error("statement", token_text); return; }
+    {   ebf_curtoken_error("statement"); return; }
 
+    /* If we don't see a keyword, this must be a function call or
+       other expression-with-side-effects. */
     if (token_type != STATEMENT_TT)
     {   put_token_back();
         AO = parse_expression(VOID_CONTEXT);
@@ -1812,8 +1835,7 @@ static void parse_statement_g(int break_label, int continue_label)
                      if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
                          break;
                      if (token_type != DQ_TT)
-                         ebf_error("text of box line in double-quotes",
-                             token_text);
+                         ebf_curtoken_error("text of box line in double-quotes");
                      {   int i, j;
                          for (i=0, j=0; token_text[i] != 0; j++)
                              if (token_text[i] == '@')
@@ -1877,7 +1899,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  get_next_token();
                  if ((token_type == STATEMENT_TT)
                      && (token_value == UNTIL_CODE))
-                 {   assemble_label_no(ln2);
+                 {   assemble_forward_label_no(ln2);
                      match_open_bracket();
                      AO = parse_expression(CONDITION_CONTEXT);
                      match_close_bracket();
@@ -1885,7 +1907,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  }
                  else error("'do' without matching 'until'");
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  break;
 
     /*  -------------------------------------------------------------------- */
@@ -1899,7 +1921,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  if ((token_type != MISC_KEYWORD_TT)
                      || ((token_value != ON_MK)
                          && (token_value != OFF_MK)))
-                 {   ebf_error("'on' or 'off'", token_text);
+                 {   ebf_curtoken_error("'on' or 'off'");
                      panic_mode_error_recovery();
                      break;
                  }
@@ -1932,6 +1954,10 @@ static void parse_statement_g(int break_label, int continue_label)
                  get_next_token();
 
                  /*  Initialisation code  */
+                 AO.type = OMITTED_OT;
+                 spare_debug_location1 = statement_debug_location;
+                 AO2.type = OMITTED_OT; flag = 0;
+                 spare_debug_location2 = statement_debug_location;
 
                  if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
                  {   put_token_back();
@@ -1951,10 +1977,9 @@ static void parse_statement_g(int break_label, int continue_label)
                              sequence_point_follows = FALSE;
                              if (!execution_never_reaches_here)
                                  assembleg_jump(ln);
-                             assemble_label_no(ln2);
+                             assemble_forward_label_no(ln2);
                              return;
                          }
-                         AO.type = OMITTED_OT;
                          goto ParseUpdate;
                      }
                      put_token_back();
@@ -1962,7 +1987,6 @@ static void parse_statement_g(int break_label, int continue_label)
                  }
 
                  get_next_token();
-                 AO.type = OMITTED_OT;
                  if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
                  {   put_token_back();
                      spare_debug_location1 = get_token_location();
@@ -1972,7 +1996,6 @@ static void parse_statement_g(int break_label, int continue_label)
                  get_next_token();
 
                  ParseUpdate:
-                 AO2.type = OMITTED_OT; flag = 0;
                  if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
                  {   put_token_back();
                      spare_debug_location2 = get_token_location();
@@ -2067,7 +2090,7 @@ static void parse_statement_g(int break_label, int continue_label)
                      }
                  }
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2077,6 +2100,7 @@ static void parse_statement_g(int break_label, int continue_label)
         case GIVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                           QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
                  if ((AO.type == LOCALVAR_OT) && (AO.value == 0))
                      onstack = TRUE;
                  else
@@ -2094,15 +2118,12 @@ static void parse_statement_g(int break_label, int continue_label)
                      if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
                          ln = 0;
                      else
-                     {   if ((token_type == SYMBOL_TT)
-                             && (stypes[token_value] != ATTRIBUTE_T))
-                           warning_named("This is not a declared Attribute:",
-                             token_text);
-                         ln = 1;
+                     {   ln = 1;
                          put_token_back();
                      }
                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1);
+                     check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
                      if (runtime_error_checking_switch && (!veneer_mode))
                      {   ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR);
                          if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
@@ -2149,8 +2170,9 @@ static void parse_statement_g(int break_label, int continue_label)
     /*  -------------------------------------------------------------------- */
 
         case IF_CODE:
-                 flag = FALSE;
+                 flag = FALSE; /* set if there's an "else" */
                  ln2 = 0;
+                 pre_unreach = execution_never_reaches_here;
 
                  match_open_bracket();
                  AO = parse_expression(CONDITION_CONTEXT);
@@ -2168,14 +2190,23 @@ static void parse_statement_g(int break_label, int continue_label)
                      ln = next_label++;
                  }
 
+                 /* The condition */
                  code_generate(AO, CONDITION_CONTEXT, ln);
 
+                 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
+                     /* If the condition never falls through to here, then
+                        it was an "if (0)" test. Our convention is to skip
+                        the "not reached" warnings for this case. */
+                     execution_never_reaches_here |= EXECSTATE_NOWARN;
+                 }
+
+                 /* The "if" block */
                  if (ln >= 0) parse_code_block(break_label, continue_label, 0);
                  else
                  {   get_next_token();
                      if ((token_type != SEP_TT)
                          || (token_value != SEMICOLON_SEP))
-                     {   ebf_error("';'", token_text);
+                     {   ebf_curtoken_error("';'");
                          put_token_back();
                      }
                  }
@@ -2202,11 +2233,44 @@ static void parse_statement_g(int break_label, int continue_label)
                  }
                  else put_token_back();
 
-                 if (ln >= 0) assemble_label_no(ln);
+                 /* The "else" label (or end of statement, if there is no "else") */
+                 labelexists = FALSE;
+                 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
 
                  if (flag)
-                 {   parse_code_block(break_label, continue_label, 0);
-                     if (ln >= 0) assemble_label_no(ln2);
+                 {
+                     /* If labelexists is false, then we started with
+                        "if (1)". In this case, we don't want a "not
+                        reached" warning on the "else" block. We
+                        temporarily disable the NOWARN flag, and restore it
+                        afterwards. */
+                     int saved_unreach = 0;
+                     if (execution_never_reaches_here && !labelexists) {
+                         saved_unreach = execution_never_reaches_here;
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+
+                     /* The "else" block */
+                     parse_code_block(break_label, continue_label, 0);
+
+                     if (execution_never_reaches_here && !labelexists) {
+                         if (saved_unreach & EXECSTATE_NOWARN)
+                             execution_never_reaches_here |= EXECSTATE_NOWARN;
+                         else
+                             execution_never_reaches_here &= ~EXECSTATE_NOWARN;
+                     }
+
+                     /* The post-"else" label */
+                     if (ln >= 0) assemble_forward_label_no(ln2);
+                 }
+                 else
+                 {
+                     /* There was no "else". If we're unreachable, then the
+                        statement returned unconditionally, which means 
+                        "if (1) return". Skip warnings. */
+                     if (!pre_unreach && execution_never_reaches_here) {
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
                  }
 
                  return;
@@ -2279,7 +2343,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  misc_keywords.enabled = FALSE;
                  if ((token_type != MISC_KEYWORD_TT)
                      || (token_value != TO_MK))
-                 {   ebf_error("'to'", token_text);
+                 {   ebf_curtoken_error("'to'");
                      panic_mode_error_recovery();
                      return;
                  }
@@ -2287,6 +2351,8 @@ static void parse_statement_g(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  AO = code_generate(AO, QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
+                 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                      assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2,
                          zero_operand);
@@ -2316,11 +2382,11 @@ static void parse_statement_g(int break_label, int continue_label)
                      INITAOTV(&AO, LOCALVAR_OT, token_value);
                  }
                  else if ((token_type == SYMBOL_TT) &&
-                   (stypes[token_value] == GLOBAL_VARIABLE_T)) {
-                     INITAOTV(&AO, GLOBALVAR_OT, svals[token_value]);
+                   (symbols[token_value].type == GLOBAL_VARIABLE_T)) {
+                     INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
                  }
                  else {
-                     ebf_error("'objectloop' variable", token_text);
+                     ebf_curtoken_error("'objectloop' variable");
                      panic_mode_error_recovery(); 
                      break;
                  }
@@ -2417,10 +2483,16 @@ static void parse_statement_g(int break_label, int continue_label)
                  }
 
                  sequence_point_follows = TRUE;
-                 ln = symbol_index("Class", -1);
-                 INITAOT(&AO2, CONSTANT_OT);
-                 AO2.value = svals[ln];
-                 AO2.marker = OBJECT_MV;
+                 ln = get_symbol_index("Class");
+                 if (ln < 0) {
+                     error("No 'Class' object found");
+                     AO2 = zero_operand;
+                 }
+                 else {
+                     INITAOT(&AO2, CONSTANT_OT);
+                     AO2.value = symbols[ln].value;
+                     AO2.marker = OBJECT_MV;
+                 }
                  assembleg_store(AO, AO2);
 
                  assemble_label_no(ln = next_label++);
@@ -2469,6 +2541,7 @@ static void parse_statement_g(int break_label, int continue_label)
         case REMOVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                      assembleg_call_1(veneer_routine(RT__ChR_VR), AO,
                          zero_operand);
@@ -2539,14 +2612,18 @@ static void parse_statement_g(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  if (is_constant_ot(AO2.type) && AO2.marker == 0) {
+                     /* Compile-time check */
                      if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
-                         memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS);
+                         error_max_dynamic_strings(AO2.value);
                      }
                  }
                  get_next_token();
                  if (token_type == DQ_TT)
                  {   INITAOT(&AO4, CONSTANT_OT);
-                     AO4.value = compile_string(token_text, TRUE, TRUE);
+                     /* This is not actually placed in low memory; Glulx
+                        has no such concept. We use the LOWSTRING flag
+                        for compatibility with older compiler behavior. */
+                     AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
                      AO4.marker = STRING_MV;
                  }
                  else
@@ -2571,9 +2648,8 @@ static void parse_statement_g(int break_label, int continue_label)
                          && (token_value != BOLD_MK)
                          && (token_value != UNDERLINE_MK)
                          && (token_value != FIXED_MK)))
-                 {   ebf_error(
-"'roman', 'bold', 'underline', 'reverse' or 'fixed'",
-                         token_text);
+                 {   ebf_curtoken_error(
+"'roman', 'bold', 'underline', 'reverse' or 'fixed'");
                      panic_mode_error_recovery();
                      break;
                  }
@@ -2622,7 +2698,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  assembleg_store(temp_var1, AO); 
 
                  parse_code_block(ln = next_label++, continue_label, 1);
-                 assemble_label_no(ln);
+                 assemble_forward_label_no(ln);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2640,7 +2716,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  parse_code_block(ln2, ln, 0);
                  sequence_point_follows = FALSE;
                  assembleg_jump(ln);
-                 assemble_label_no(ln2);
+                 assemble_forward_label_no(ln2);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2667,17 +2743,75 @@ static void parse_statement_g(int break_label, int continue_label)
 
     get_next_token();
     if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
-    {   ebf_error("';'", token_text);
+    {   ebf_curtoken_error("';'");
         put_token_back();
     }
 }
 
 extern void parse_statement(int break_label, int continue_label)
 {
-  if (!glulx_mode)
-    parse_statement_z(break_label, continue_label);
-  else
-    parse_statement_g(break_label, continue_label);
+    int res;
+    int saved_entire_flag;
+    
+    res = parse_named_label_statements();
+    if (!res)
+        return;
+
+    saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
+    if (execution_never_reaches_here)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    if (!glulx_mode)
+        parse_statement_z(break_label, continue_label);
+    else
+        parse_statement_g(break_label, continue_label);
+
+    if (saved_entire_flag)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    else
+        execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
+}
+
+/* This does the same work as parse_statement(), but it's called if you've
+   already parsed an expression (in void context) and you want to generate
+   it as a statement. Essentially it's a copy of parse_statement() and
+   parse_statement_z/g(), except we skip straight to the "expression-with-
+   side-effects" bit and omit everything else.
+
+   The caller doesn't need to pass break_label/continue_label; they're
+   not used for this code path.
+*/
+extern void parse_statement_singleexpr(assembly_operand AO)
+{
+    int res;
+    int saved_entire_flag;
+    
+    res = parse_named_label_statements();
+    if (!res)
+        return;
+
+    saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
+    if (execution_never_reaches_here)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+
+    code_generate(AO, VOID_CONTEXT, -1);
+    
+    if (vivc_flag) {
+        panic_mode_error_recovery();
+    }
+    else {
+        /* StatementTerminator... */
+        get_next_token();
+        if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
+        {   ebf_curtoken_error("';'");
+            put_token_back();
+        }
+    }
+
+    if (saved_entire_flag)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    else
+        execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
 }
 
 /* ========================================================================= */