/* ------------------------------------------------------------------------- */
/*   "states" :  Statement translator                                        */
/*                                                                           */
/*   Part of Inform 6.41                                                     */
/*   copyright (c) Graham Nelson 1993 - 2022                                 */
/*                                                                           */
/* ------------------------------------------------------------------------- */

#include "header.h"

static int match_colon(void)
{   get_next_token();
    if (token_type == SEP_TT)
    {   if (token_value == SEMICOLON_SEP)
            warning("Unlike C, Inform uses ':' to divide parts \
of a 'for' loop specification: replacing ';' with ':'");
        else
        if (token_value != COLON_SEP)
        {   ebf_error("':'", token_text);
            panic_mode_error_recovery();
            return(FALSE);
        }
    }
    else
    {   ebf_error("':'", token_text);
        panic_mode_error_recovery();
        return(FALSE);
    }
    return(TRUE);
}

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);
}

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);
}

static void parse_action(void)
{   int level = 1, args = 0, codegen_action;
    assembly_operand AO, AO2, AO3, AO4, AO5;

    /* An action statement has the form <ACTION NOUN SECOND, ACTOR>
       or <<ACTION NOUN SECOND, ACTOR>>. It simply compiles into a call
       to R_Process() with those four arguments. (The latter form,
       with double brackets, means "return true afterwards".)

       The R_Process() function should be supplied by the library, 
       although a stub is defined in the veneer.

       The NOUN, SECOND, and ACTOR arguments are optional. If not
       supplied, R_Process() will be called with fewer arguments. 
       (But if you supply ACTOR, it must be preceded by a comma.
       <ACTION, ACTOR> is equivalent to <ACTION 0 0, ACTOR>.)

       To complicate life, the ACTION argument may be a bare action
       name or a parenthesized expression. (So <Take> is equivalent
       to <(##Take)>.) We have to peek at the first token, checking
       whether it's an open-paren, to distinguish these cases.

       You may ask why the ACTOR argument is last; the "natural"
       Inform ordering would be "<floyd, take ball>". True! Sadly,
       Inform's lexer isn't smart enough to parse this consistently,
       so we can't do it.
    */

    dont_enter_into_symbol_table = TRUE;
    get_next_token();
    if ((token_type == SEP_TT) && (token_value == LESS_SEP))
    {   level = 2; get_next_token();
    }
    dont_enter_into_symbol_table = FALSE;

    /* Peek at the next token; see if it's an open-paren. */
    if ((token_type==SEP_TT) && (token_value==OPENB_SEP))
    {   put_token_back();
        AO2 = parse_expression(ACTION_Q_CONTEXT);
        codegen_action = TRUE;
    }
    else
    {   codegen_action = FALSE;
        AO2 = action_of_name(token_text);
    }

    get_next_token();
    AO3 = zero_operand;
    AO4 = zero_operand;
    AO5 = zero_operand;
    if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
    {   put_token_back();
        args = 1;
        AO3 = parse_expression(ACTION_Q_CONTEXT);

        get_next_token();
    }
    if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
    {   put_token_back();
        args = 2;
        AO4 = parse_expression(QUANTITY_CONTEXT);
        get_next_token();
    }
    if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
    {
        ebf_error("',' or '>'", token_text);
    }

    if ((token_type == SEP_TT) && (token_value == COMMA_SEP))
    {
        if (!glulx_mode && (version_number < 4))
        {
            error("<x, y> syntax is not available in Z-code V3 or earlier");
        }
        args = 3;
        AO5 = parse_expression(QUANTITY_CONTEXT);
        get_next_token();
        if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
        {
            ebf_error("'>'", token_text);
        }
    }

    if (level == 2)
    {   get_next_token();
        if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
        {   put_token_back();
            ebf_error("'>>'", token_text);
        }
    }

    if (!glulx_mode) {

      AO = veneer_routine(R_Process_VR);

      switch(args)
      {   case 0:
            if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
            if (version_number>=5)
                assemblez_2(call_2n_zc, AO, AO2);
            else
            if (version_number==4)
                assemblez_2_to(call_vs_zc, AO, AO2, temp_var1);
            else
                assemblez_2_to(call_zc, AO, AO2, temp_var1);
            break;
          case 1:
            AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
            if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
            if (version_number>=5)
                assemblez_3(call_vn_zc, AO, AO2, AO3);
            else
            if (version_number==4)
                assemblez_3_to(call_vs_zc, AO, AO2, AO3, temp_var1);
            else
                assemblez_3_to(call_zc, AO, AO2, AO3, temp_var1);
            break;
          case 2:
            AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
            AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
            if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
            if (version_number>=5)
                assemblez_4(call_vn_zc, AO, AO2, AO3, AO4);
            else
            if (version_number==4)
                assemblez_4_to(call_vs_zc, AO, AO2, AO3, AO4, temp_var1);
            else
                assemblez_4_to(call_zc, AO, AO2, AO3, AO4, temp_var1);
            break;
          case 3:
            AO5 = code_generate(AO5, QUANTITY_CONTEXT, -1);
            AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
            AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
            if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
            if (version_number>=5)
                assemblez_5(call_vn2_zc, AO, AO2, AO3, AO4, AO5);
            else
            if (version_number==4)
                assemblez_5_to(call_vs2_zc, AO, AO2, AO3, AO4, AO5, temp_var1);
            /* if V3 or earlier, we've already displayed an error */
            break;
            break;
      }

      if (level == 2) assemblez_0(rtrue_zc);

    }
    else {

      AO = veneer_routine(R_Process_VR);

      switch (args) {

      case 0:
        if (codegen_action) 
          AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
        assembleg_call_1(AO, AO2, zero_operand);
        break;

      case 1:
        AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
        if (codegen_action)
          AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
        assembleg_call_2(AO, AO2, AO3, zero_operand);
        break;

      case 2:
        AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
        AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
        if (codegen_action) 
          AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
        assembleg_call_3(AO, AO2, AO3, AO4, zero_operand);
        break;

      case 3:
        AO5 = code_generate(AO5, QUANTITY_CONTEXT, -1);
        if (!((AO5.type == LOCALVAR_OT) && (AO5.value == 0)))
            assembleg_store(stack_pointer, AO5);
        AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
        if (!((AO4.type == LOCALVAR_OT) && (AO4.value == 0)))
            assembleg_store(stack_pointer, AO4);
        AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
        if (!((AO3.type == LOCALVAR_OT) && (AO3.value == 0)))
            assembleg_store(stack_pointer, AO3);
        if (codegen_action) 
          AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
        if (!((AO2.type == LOCALVAR_OT) && (AO2.value == 0)))
          assembleg_store(stack_pointer, AO2);
        assembleg_3(call_gc, AO, four_operand, zero_operand);
        break;
      }

      if (level == 2) 
        assembleg_1(return_gc, one_operand);

    }
}

extern int parse_label(void)
{
    get_next_token();

    if ((token_type == SYMBOL_TT) &&
        (symbols[token_value].type == LABEL_T))
    {   symbols[token_value].flags |= USED_SFLAG;
        return(symbols[token_value].value);
    }

    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++;
        symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG;
        return(symbols[token_value].value);
    }

    ebf_error("label name", token_text);
    return 0;
}

static void parse_print_z(int finally_return)
{   int count = 0; assembly_operand AO;

    /*  print <printlist> -------------------------------------------------- */
    /*  print_ret <printlist> ---------------------------------------------- */
    /*  <literal-string> --------------------------------------------------- */
    /*                                                                       */
    /*  <printlist> is a comma-separated list of items:                      */
    /*                                                                       */
    /*       <literal-string>                                                */
    /*       <other-expression>                                              */
    /*       (char) <expression>                                             */
    /*       (address) <expression>                                          */
    /*       (string) <expression>                                           */
    /*       (a) <expression>                                                */
    /*       (the) <expression>                                              */
    /*       (The) <expression>                                              */
    /*       (name) <expression>                                             */
    /*       (number) <expression>                                           */
    /*       (property) <expression>                                         */
    /*       (<routine>) <expression>                                        */
    /*       (object) <expression>     (for use in low-level code only)      */
    /* --------------------------------------------------------------------- */

    do
    {   AI.text = token_text;
        if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
        switch(token_type)
        {   case DQ_TT:
              if (strlen(token_text) > 32)
              {   INITAOT(&AO, LONG_CONSTANT_OT);
                  AO.marker = STRING_MV;
                  AO.value  = compile_string(token_text, STRCTX_GAME);
                  assemblez_1(print_paddr_zc, AO);
                  if (finally_return)
                  {   get_next_token();
                      if ((token_type == SEP_TT)
                          && (token_value == SEMICOLON_SEP))
                      {   assemblez_0(new_line_zc);
                          assemblez_0(rtrue_zc);
                          return;
                      }
                      put_token_back();
                  }
                  break;
              }
              if (finally_return)
              {   get_next_token();
                  if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
                  {   assemblez_0(print_ret_zc); return;
                  }
                  put_token_back();
              }
              assemblez_0(print_zc);
              break;

            case SEP_TT:
              if (token_value == OPENB_SEP)
              {   misc_keywords.enabled = TRUE;
                  get_next_token();
                  get_next_token();
                  if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
                  {   assembly_operand AO1;

                      put_token_back(); put_token_back();
                      local_variables.enabled = FALSE;
                      get_next_token();
                      misc_keywords.enabled = FALSE;
                      local_variables.enabled = TRUE;

                      if ((token_type == STATEMENT_TT)
                          &&(token_value == STRING_CODE))
                      {   token_type = MISC_KEYWORD_TT;
                          token_value = STRING_MK;
                      }

                      switch(token_type)
                      {
                        case MISC_KEYWORD_TT:
                          switch(token_value)
                          {   case CHAR_MK:
                                  if (runtime_error_checking_switch)
                                  {   AO = veneer_routine(RT__ChPrintC_VR);
                                      goto PrintByRoutine;
                                  }
                                  get_next_token();
                                  AO1 = code_generate(
                                      parse_expression(QUANTITY_CONTEXT),
                                      QUANTITY_CONTEXT, -1);
                                  assemblez_1(print_char_zc, AO1);
                                  goto PrintTermDone;
                              case ADDRESS_MK:
                                  if (runtime_error_checking_switch)
                                  {   AO = veneer_routine(RT__ChPrintA_VR);
                                      goto PrintByRoutine;
                                  }
                                  get_next_token();
                                  AO1 = code_generate(
                                      parse_expression(QUANTITY_CONTEXT),
                                      QUANTITY_CONTEXT, -1);
                                  assemblez_1(print_addr_zc, AO1);
                                  goto PrintTermDone;
                              case STRING_MK:
                                  if (runtime_error_checking_switch)
                                  {   AO = veneer_routine(RT__ChPrintS_VR);
                                      goto PrintByRoutine;
                                  }
                                  get_next_token();
                                  AO1 = code_generate(
                                      parse_expression(QUANTITY_CONTEXT),
                                      QUANTITY_CONTEXT, -1);
                                  assemblez_1(print_paddr_zc, AO1);
                                  goto PrintTermDone;
                              case OBJECT_MK:
                                  if (runtime_error_checking_switch)
                                  {   AO = veneer_routine(RT__ChPrintO_VR);
                                      goto PrintByRoutine;
                                  }
                                  get_next_token();
                                  AO1 = code_generate(
                                      parse_expression(QUANTITY_CONTEXT),
                                      QUANTITY_CONTEXT, -1);
                                  assemblez_1(print_obj_zc, AO1);
                                  goto PrintTermDone;
                              case THE_MK:
                                  AO = veneer_routine(DefArt_VR);
                                  goto PrintByRoutine;
                              case AN_MK:
                              case A_MK:
                                  AO = veneer_routine(InDefArt_VR);
                                  goto PrintByRoutine;
                              case CAP_THE_MK:
                                  AO = veneer_routine(CDefArt_VR);
                                  goto PrintByRoutine;
                              case CAP_A_MK:
                                  AO = veneer_routine(CInDefArt_VR);
                                  goto PrintByRoutine;
                              case NAME_MK:
                                  AO = veneer_routine(PrintShortName_VR);
                                  goto PrintByRoutine;
                              case NUMBER_MK:
                                  AO = veneer_routine(EnglishNumber_VR);
                                  goto PrintByRoutine;
                              case PROPERTY_MK:
                                  AO = veneer_routine(Print__Pname_VR);
                                  goto PrintByRoutine;
                              default:
               error_named("A reserved word was used as a print specification:",
                                      token_text);
                          }
                          break;

                        case SYMBOL_TT:
                          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 = symbols[token_value].value;
                              AO.marker = IROUTINE_MV;
                              AO.symindex = token_value;
                              if (symbols[token_value].type != ROUTINE_T)
                                ebf_error("printing routine name", token_text);
                          }
                          symbols[token_value].flags |= USED_SFLAG;

                          PrintByRoutine:

                          get_next_token();
                          if (version_number >= 5)
                            assemblez_2(call_2n_zc, AO,
                              code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1));
                          else if (version_number == 4)
                            assemblez_2_to(call_vs_zc, AO,
                              code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1), temp_var1);
                          else
                            assemblez_2_to(call_zc, AO,
                              code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1), temp_var1);
                          goto PrintTermDone;

                        default: ebf_error("print specification", token_text);
                          get_next_token();
                          assemblez_1(print_num_zc,
                          code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1));
                          goto PrintTermDone;
                      }
                  }
                  put_token_back(); put_token_back(); put_token_back();
                  misc_keywords.enabled = FALSE;
                  assemblez_1(print_num_zc,
                      code_generate(parse_expression(QUANTITY_CONTEXT),
                          QUANTITY_CONTEXT, -1));
                  break;
              }

            default:
              put_token_back(); misc_keywords.enabled = FALSE;
              assemblez_1(print_num_zc,
                  code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1));
              break;
        }

        PrintTermDone: misc_keywords.enabled = FALSE;

        count++;
        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);
            panic_mode_error_recovery(); return;
        }
        else get_next_token();
    } while(TRUE);

    if (count == 0) ebf_error("something to print", token_text);
    if (finally_return)
    {   assemblez_0(new_line_zc);
        assemblez_0(rtrue_zc);
    }
}

static void parse_print_g(int finally_return)
{   int count = 0; assembly_operand AO, AO2;

    /*  print <printlist> -------------------------------------------------- */
    /*  print_ret <printlist> ---------------------------------------------- */
    /*  <literal-string> --------------------------------------------------- */
    /*                                                                       */
    /*  <printlist> is a comma-separated list of items:                      */
    /*                                                                       */
    /*       <literal-string>                                                */
    /*       <other-expression>                                              */
    /*       (char) <expression>                                             */
    /*       (address) <expression>                                          */
    /*       (string) <expression>                                           */
    /*       (a) <expression>                                                */
    /*       (A) <expression>                                                */
    /*       (the) <expression>                                              */
    /*       (The) <expression>                                              */
    /*       (name) <expression>                                             */
    /*       (number) <expression>                                           */
    /*       (property) <expression>                                         */
    /*       (<routine>) <expression>                                        */
    /*       (object) <expression>     (for use in low-level code only)      */
    /* --------------------------------------------------------------------- */

    do
    {   
        if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
        switch(token_type)
        {   case DQ_TT:
              /* 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, STRCTX_GAME);
                  assembleg_1(streamstr_gc, AO);
                  if (finally_return)
                  {   get_next_token();
                      if ((token_type == SEP_TT)
                          && (token_value == SEMICOLON_SEP))
                      {   INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
                          assembleg_1(streamchar_gc, AO); 
                          INITAOTV(&AO, BYTECONSTANT_OT, 1);
                          assembleg_1(return_gc, AO); 
                          return;
                      }
                      put_token_back();
                  }
                  break;
              }
              break;

            case SEP_TT:
              if (token_value == OPENB_SEP)
              {   misc_keywords.enabled = TRUE;
                  get_next_token();
                  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;
                      get_next_token();
                      misc_keywords.enabled = FALSE;
                      local_variables.enabled = TRUE;

                      if ((token_type == STATEMENT_TT)
                          &&(token_value == STRING_CODE))
                      {   token_type = MISC_KEYWORD_TT;
                          token_value = STRING_MK;
                      }

                      switch(token_type)
                      {
                        case MISC_KEYWORD_TT:
                          switch(token_value)
                          {   case CHAR_MK:
                                  if (runtime_error_checking_switch)
                                  {   AO = veneer_routine(RT__ChPrintC_VR);
                                      goto PrintByRoutine;
                                  }
                                  get_next_token();
                                  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);
                                  }
                                  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)
                                      AO = veneer_routine(RT__ChPrintA_VR);
                                  else
                                      AO = veneer_routine(Print__Addr_VR);
                                  goto PrintByRoutine;
                              case STRING_MK:
                                  if (runtime_error_checking_switch)
                                  {   AO = veneer_routine(RT__ChPrintS_VR);
                                      goto PrintByRoutine;
                                  }
                                  get_next_token();
                                  AO1 = code_generate(
                                      parse_expression(QUANTITY_CONTEXT),
                                      QUANTITY_CONTEXT, -1);
                                  assembleg_1(streamstr_gc, AO1);
                                  goto PrintTermDone;
                              case OBJECT_MK:
                                  if (runtime_error_checking_switch)
                                  {   AO = veneer_routine(RT__ChPrintO_VR);
                                      goto PrintByRoutine;
                                  }
                                  get_next_token();
                                  AO1 = code_generate(
                                      parse_expression(QUANTITY_CONTEXT),
                                      QUANTITY_CONTEXT, -1);
                                  INITAOT(&AO2, BYTECONSTANT_OT);
                                  AO2.value = GOBJFIELD_NAME();
                                  assembleg_3(aload_gc, AO1, AO2, 
                                    stack_pointer);
                                  assembleg_1(streamstr_gc, stack_pointer);
                                  goto PrintTermDone;
                              case THE_MK:
                                  AO = veneer_routine(DefArt_VR);
                                  goto PrintByRoutine;
                              case AN_MK:
                              case A_MK:
                                  AO = veneer_routine(InDefArt_VR);
                                  goto PrintByRoutine;
                              case CAP_THE_MK:
                                  AO = veneer_routine(CDefArt_VR);
                                  goto PrintByRoutine;
                              case CAP_A_MK:
                                  AO = veneer_routine(CInDefArt_VR);
                                  goto PrintByRoutine;
                              case NAME_MK:
                                  AO = veneer_routine(PrintShortName_VR);
                                  goto PrintByRoutine;
                              case NUMBER_MK:
                                  AO = veneer_routine(EnglishNumber_VR);
                                  goto PrintByRoutine;
                              case PROPERTY_MK:
                                  AO = veneer_routine(Print__Pname_VR);
                                  goto PrintByRoutine;
                              default:
               error_named("A reserved word was used as a print specification:",
                                      token_text);
                          }
                          break;

                        case SYMBOL_TT:
                          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 = symbols[token_value].value;
                              AO.marker = IROUTINE_MV;
                              AO.symindex = token_value;
                              if (symbols[token_value].type != ROUTINE_T)
                                ebf_error("printing routine name", token_text);
                          }
                          symbols[token_value].flags |= USED_SFLAG;

                          PrintByRoutine:

                          get_next_token();
                          INITAOT(&AO2, ZEROCONSTANT_OT);
                          assembleg_call_1(AO,
                            code_generate(parse_expression(QUANTITY_CONTEXT),
                              QUANTITY_CONTEXT, -1),
                            AO2);
                          goto PrintTermDone;

                        default: ebf_error("print specification", token_text);
                          get_next_token();
                          assembleg_1(streamnum_gc,
                          code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1));
                          goto PrintTermDone;
                      }
                  }
                  put_token_back(); put_token_back(); put_token_back();
                  misc_keywords.enabled = FALSE;
                  assembleg_1(streamnum_gc,
                      code_generate(parse_expression(QUANTITY_CONTEXT),
                          QUANTITY_CONTEXT, -1));
                  break;
              }

            default:
              put_token_back(); misc_keywords.enabled = FALSE;
              assembleg_1(streamnum_gc,
                  code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1));
              break;
        }

        PrintTermDone: misc_keywords.enabled = FALSE;

        count++;
        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);
            panic_mode_error_recovery(); return;
        }
        else get_next_token();
    } while(TRUE);

    if (count == 0) ebf_error("something to print", token_text);
    if (finally_return)
    {
        INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
        assembleg_1(streamchar_gc, AO); 
        INITAOTV(&AO, BYTECONSTANT_OT, 1);
        assembleg_1(return_gc, AO); 
    }
}

/* 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)
        {
            ebf_error("label name", token_text);
            return TRUE;
        }

        if (symbols[token_value].flags & UNKNOWN_SFLAG)
        {   assign_symbol(token_value, next_label, LABEL_T);
            symbols[token_value].flags |= USED_SFLAG;
            assemble_label_no(next_label);
            define_symbol_label(token_value);
            next_label++;
        }
        else
        {   if (symbols[token_value].type != LABEL_T) {
                ebf_error("label name", token_text);
                return TRUE;
            }
            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 FALSE;
        }

        /*  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 FALSE;
        }
        /* 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;
    }

    if ((token_type == SEP_TT) && (token_value == AT_SEP))
    {   parse_assembly(); return;
    }

    if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;

    if (token_type == DQ_TT)
    {   parse_print_z(TRUE); return;
    }

    if ((token_type == SEP_TT) && (token_value == LESS_SEP))
    {   parse_action(); goto StatementTerminator; }

    if (token_type == EOF_TT)
    {   ebf_error("statement", token_text); return; }

    if (token_type != STATEMENT_TT)
    {   put_token_back();
        AO = parse_expression(VOID_CONTEXT);
        code_generate(AO, VOID_CONTEXT, -1);
        if (vivc_flag) { panic_mode_error_recovery(); return; }
        goto StatementTerminator;
    }

    statements.enabled = FALSE;

    switch(token_value)
    {
    /*  -------------------------------------------------------------------- */
    /*  box <string-1> ... <string-n> -------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case BOX_CODE:
             if (version_number == 3)
                 warning("The 'box' statement has no effect in a version 3 game");
             INITAOT(&AO3, LONG_CONSTANT_OT);
                 AO3.value = begin_table_array();
                 AO3.marker = ARRAY_MV;
                 ln = 0; ln2 = 0;
                 do
                 {   get_next_token();
                     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);
                     {   int i, j;
                         for (i=0, j=0; token_text[i] != 0; j++)
                             if (token_text[i] == '@')
                             {   if (token_text[i+1] == '@')
                                 {   i = i + 2;
                                     while (isdigit(token_text[i])) i++;
                                 }
                                 else
                                 {   i++;
                                     if (token_text[i] != 0) i++;
                                     if (token_text[i] != 0) i++;
                                 }
                             }
                             else i++;
                         if (j > ln2) ln2 = j;
                     }
                     put_token_back();
                     array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
                 } while (TRUE);
                 finish_array(ln, FALSE);
                 if (ln == 0)
                     error("No lines of text given for 'box' display");

                 if (version_number == 3) return;

                 INITAOTV(&AO2, SHORT_CONSTANT_OT, ln2);
                 INITAOTV(&AO4, VARIABLE_OT, 255);
                 assemblez_3_to(call_vs_zc, veneer_routine(Box__Routine_VR),
                     AO2, AO3, AO4);
                 return;

    /*  -------------------------------------------------------------------- */
    /*  break -------------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case BREAK_CODE:
                 if (break_label == -1)
                 error("'break' can only be used in a loop or 'switch' block");
                 else
                     assemblez_jump(break_label);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  continue ----------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case CONTINUE_CODE:
                 if (continue_label == -1)
                 error("'continue' can only be used in a loop block");
                 else
                     assemblez_jump(continue_label);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  do <codeblock> until (<condition>) --------------------------------- */
    /*  -------------------------------------------------------------------- */

        case DO_CODE:
                 assemble_label_no(ln = next_label++);
                 ln2 = next_label++; ln3 = next_label++;
                 parse_code_block(ln3, ln2, 0);
                 statements.enabled = TRUE;
                 get_next_token();
                 if ((token_type == STATEMENT_TT)
                     && (token_value == UNTIL_CODE))
                 {   assemble_forward_label_no(ln2);
                     match_open_bracket();
                     AO = parse_expression(CONDITION_CONTEXT);
                     match_close_bracket();
                     code_generate(AO, CONDITION_CONTEXT, ln);
                 }
                 else error("'do' without matching 'until'");

                 assemble_forward_label_no(ln3);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  font on/off -------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case FONT_CODE:
                 misc_keywords.enabled = TRUE;
                 get_next_token();
                 misc_keywords.enabled = FALSE;
                 if ((token_type != MISC_KEYWORD_TT)
                     || ((token_value != ON_MK)
                         && (token_value != OFF_MK)))
                 {   ebf_error("'on' or 'off'", token_text);
                     panic_mode_error_recovery();
                     break;
                 }

                 if (version_number >= 5)
                 {   /* Use the V5 @set_font opcode, setting font 4
                        (for font off) or 1 (for font on). */
                     INITAOT(&AO, SHORT_CONSTANT_OT);
                     if (token_value == ON_MK)
                         AO.value = 1;
                     else
                         AO.value = 4;
                     assemblez_1_to(set_font_zc, AO, temp_var1);
                     break;
                 }

                 /* Set the fixed-pitch header bit. */
                 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
                 INITAOTV(&AO2, SHORT_CONSTANT_OT, 8);
                 INITAOTV(&AO3, VARIABLE_OT, 255);
                 assemblez_2_to(loadw_zc, AO, AO2, AO3);

                 if (token_value == ON_MK)
                 {   INITAOTV(&AO4, LONG_CONSTANT_OT, 0xfffd);
                     assemblez_2_to(and_zc, AO4, AO3, AO3);
                 }
                 else
                 {   INITAOTV(&AO4, SHORT_CONSTANT_OT, 2);
                     assemblez_2_to(or_zc, AO4, AO3, AO3);
                 }

                 assemblez_3(storew_zc, AO, AO2, AO3);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  for (<initialisation> : <continue-condition> : <updating>) --------- */
    /*  -------------------------------------------------------------------- */

        /*  Note that it's legal for any or all of the three sections of a
            'for' specification to be empty.  This 'for' implementation
            often wastes 3 bytes with a redundant branch rather than keep
            expression parse trees for long periods (as previous versions
            of Inform did, somewhat crudely by simply storing the textual
            form of a 'for' loop).  It is adequate for now.                  */

        case FOR_CODE:
                 match_open_bracket();
                 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();
                     if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
                     {   sequence_point_follows = TRUE;
                         statement_debug_location = get_token_location();
                         code_generate(parse_expression(FORINIT_CONTEXT),
                             VOID_CONTEXT, -1);
                     }
                     get_next_token();
                     if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
                     {   get_next_token();
                         if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
                         {   assemble_label_no(ln = next_label++);
                             ln2 = next_label++;
                             parse_code_block(ln2, ln, 0);
                             sequence_point_follows = FALSE;
                             if (!execution_never_reaches_here)
                                 assemblez_jump(ln);
                             assemble_forward_label_no(ln2);
                             return;
                         }
                         goto ParseUpdate;
                     }
                     put_token_back();
                     if (!match_colon()) break;
                 }

                 get_next_token();
                 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
                 {   put_token_back();
                     spare_debug_location1 = get_token_location();
                     AO = parse_expression(CONDITION_CONTEXT);
                     if (!match_colon()) break;
                 }
                 get_next_token();

                 ParseUpdate:
                 if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
                 {   put_token_back();
                     spare_debug_location2 = get_token_location();
                     AO2 = parse_expression(VOID_CONTEXT);
                     match_close_bracket();
                     flag = test_for_incdec(AO2);
                 }

                 ln = next_label++;
                 ln2 = next_label++;
                 ln3 = next_label++;

                 if ((AO2.type == OMITTED_OT) || (flag != 0))
                 {
                     assemble_label_no(ln);
                     if (flag==0) assemble_label_no(ln2);

                     /*  The "finished yet?" condition  */

                     if (AO.type != OMITTED_OT)
                     {   sequence_point_follows = TRUE;
                         statement_debug_location = spare_debug_location1;
                         code_generate(AO, CONDITION_CONTEXT, ln3);
                     }

                 }
                 else
                 {
                     /*  This is the jump which could be avoided with the aid
                         of long-term expression storage  */

                     sequence_point_follows = FALSE;
                     assemblez_jump(ln2);

                     /*  The "update" part  */

                     assemble_label_no(ln);
                     sequence_point_follows = TRUE;
                     statement_debug_location = spare_debug_location2;
                     code_generate(AO2, VOID_CONTEXT, -1);

                     assemble_label_no(ln2);

                     /*  The "finished yet?" condition  */

                     if (AO.type != OMITTED_OT)
                     {   sequence_point_follows = TRUE;
                         statement_debug_location = spare_debug_location1;
                         code_generate(AO, CONDITION_CONTEXT, ln3);
                     }
                 }

                 if (flag != 0)
                 {
                     /*  In this optimised case, update code is at the end
                         of the loop block, so "continue" goes there  */

                     parse_code_block(ln3, ln2, 0);
                     assemble_label_no(ln2);

                     sequence_point_follows = TRUE;
                     statement_debug_location = spare_debug_location2;
                     if (flag > 0)
                     {   INITAOTV(&AO3, SHORT_CONSTANT_OT, flag);
                         assemblez_1(inc_zc, AO3);
                     }
                     else
                     {   INITAOTV(&AO3, SHORT_CONSTANT_OT, -flag);
                         assemblez_1(dec_zc, AO3);
                     }
                     assemblez_jump(ln);
                 }
                 else
                 {
                     /*  In the unoptimised case, update code is at the
                         start of the loop block, so "continue" goes there  */

                     parse_code_block(ln3, ln, 0);
                     if (!execution_never_reaches_here)
                     {   sequence_point_follows = FALSE;
                         assemblez_jump(ln);
                     }
                 }

                 assemble_forward_label_no(ln3);
                 return;

    /*  -------------------------------------------------------------------- */
    /*  give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
    /*  -------------------------------------------------------------------- */

        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);
                     else assemblez_0_to(pull_zc, AO);
                     AO.type = VARIABLE_OT;
                 }

                 do
                 {   get_next_token();
                     if ((token_type == SEP_TT)&&(token_value == SEMICOLON_SEP))
                         return;
                     if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
                         ln = clear_attr_zc;
                     else
                     {   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)
                             assemblez_3(call_vn_zc, veneer_routine(ln2),
                             AO, AO2);
                         else
                         {   
                             assemblez_3_to(call_zc, veneer_routine(ln2),
                                 AO, AO2, temp_var1);
                         }
                     }
                     else
                         assemblez_2(ln, AO, AO2);
                 } while(TRUE);

    /*  -------------------------------------------------------------------- */
    /*  if (<condition>) <codeblock> [else <codeblock>] -------------------- */
    /*  -------------------------------------------------------------------- */

        case IF_CODE:
                 flag = FALSE; /* set if there's an "else" */
                 ln2 = 0;
                 pre_unreach = execution_never_reaches_here;

                 match_open_bracket();
                 AO = parse_expression(CONDITION_CONTEXT);
                 match_close_bracket();

                 statements.enabled = TRUE;
                 get_next_token();
                 if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
                     ln = -4;
                 else
                 if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
                     ln = -3;
                 else
                 {   put_token_back();
                     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);
                         put_token_back();
                     }
                 }

                 statements.enabled = TRUE;
                 get_next_token();

                 /* An #if directive around the ELSE clause is legal. */
                 while ((token_type == SEP_TT) && (token_value == HASH_SEP))
                 {   parse_directive(TRUE);
                     statements.enabled = TRUE;
                     get_next_token();
                 }
                 
                 if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
                 {   flag = TRUE;
                     if (ln >= 0)
                     {   ln2 = next_label++;
                         if (!execution_never_reaches_here)
                         {   sequence_point_follows = FALSE;
                             assemblez_jump(ln2);
                         }
                     }
                 }
                 else put_token_back();

                 /* 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)
                 {
                     /* 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;

    /*  -------------------------------------------------------------------- */
    /*  inversion ---------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case INVERSION_CODE:
                 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
                 INITAOT(&AO2, SHORT_CONSTANT_OT);

                 AO2.value  = 60;
                 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
                 assemblez_1(print_char_zc, temp_var1);
                 AO2.value  = 61;
                 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
                 assemblez_1(print_char_zc, temp_var1);
                 AO2.value  = 62;
                 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
                 assemblez_1(print_char_zc, temp_var1);
                 AO2.value  = 63;
                 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
                 assemblez_1(print_char_zc, temp_var1);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  jump <label> ------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case JUMP_CODE:
                 assemblez_jump(parse_label());
                 break;

    /*  -------------------------------------------------------------------- */
    /*  move <expression> to <expression> ---------------------------------- */
    /*  -------------------------------------------------------------------- */

        case MOVE_CODE:
                 misc_keywords.enabled = TRUE;
                 AO = parse_expression(QUANTITY_CONTEXT);

                 get_next_token();
                 misc_keywords.enabled = FALSE;
                 if ((token_type != MISC_KEYWORD_TT)
                     || (token_value != TO_MK))
                 {   ebf_error("'to'", token_text);
                     panic_mode_error_recovery();
                     return;
                 }

                 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),
                             AO, AO2);
                     else
                     {   assemblez_3_to(call_zc, veneer_routine(RT__ChT_VR),
                             AO, AO2, temp_var1);
                     }
                 }
                 else
                     assemblez_2(insert_obj_zc, AO, AO2);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  new_line ----------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case NEW_LINE_CODE:  assemblez_0(new_line_zc); break;

    /*  -------------------------------------------------------------------- */
    /*  objectloop (<initialisation>) <codeblock> -------------------------- */
    /*  -------------------------------------------------------------------- */

        case OBJECTLOOP_CODE:

                 match_open_bracket();
                 get_next_token();
                 INITAOT(&AO, VARIABLE_OT);
                 if (token_type == LOCAL_VARIABLE_TT)
                     AO.value = token_value;
                 else
                 if ((token_type == SYMBOL_TT) &&
                     (symbols[token_value].type == GLOBAL_VARIABLE_T))
                     AO.value = symbols[token_value].value;
                 else
                 {   ebf_error("'objectloop' variable", token_text);
                     panic_mode_error_recovery(); break;
                 }
                 misc_keywords.enabled = TRUE;
                 get_next_token(); flag = TRUE;
                 misc_keywords.enabled = FALSE;
                 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
                     flag = FALSE;

                 ln = 0;
                 if ((token_type == MISC_KEYWORD_TT)
                     && (token_value == NEAR_MK)) ln = 1;
                 if ((token_type == MISC_KEYWORD_TT)
                     && (token_value == FROM_MK)) ln = 2;
                 if ((token_type == CND_TT) && (token_value == IN_COND))
                 {   get_next_token();
                     get_next_token();
                     if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
                         ln = 3;
                     put_token_back();
                     put_token_back();
                 }

                 if (ln > 0)
                 {   /*  Old style (Inform 5) objectloops: note that we
                         implement objectloop (a in b) in the old way since
                         this runs through objects in a different order from
                         the new way, and there may be existing Inform code
                         relying on this.                                    */
                     assembly_operand AO4;
                     INITAO(&AO4);

                     sequence_point_follows = TRUE;
                     AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                         QUANTITY_CONTEXT, -1);
                     match_close_bracket();
                     if (ln == 1)
                     {   INITAOTV(&AO3, VARIABLE_OT, 0);
                         if (runtime_error_checking_switch)
                                 AO2 = check_nonzero_at_runtime(AO2, -1,
                                     OBJECTLOOP_RTE);
                         assemblez_1_to(get_parent_zc, AO2, AO3);
                         assemblez_objcode(get_child_zc, AO3, AO3, -2, TRUE);
                         AO2 = AO3;
                     }
                     if (ln == 3)
                     {   INITAOTV(&AO3, VARIABLE_OT, 0);
                         if (runtime_error_checking_switch)
                         {   AO4 = AO2;
                             AO2 = check_nonzero_at_runtime(AO2, -1,
                                 CHILD_RTE);
                         }
                         assemblez_objcode(get_child_zc, AO2, AO3, -2, TRUE);
                         AO2 = AO3;
                     }
                     assemblez_store(AO, AO2);
                     assemblez_1_branch(jz_zc, AO, ln2 = next_label++, TRUE);
                     assemble_label_no(ln4 = next_label++);
                     parse_code_block(ln2, ln3 = next_label++, 0);
                     sequence_point_follows = FALSE;
                     assemble_label_no(ln3);
                     if (runtime_error_checking_switch)
                     {   AO2 = check_nonzero_at_runtime(AO, ln2,
                              OBJECTLOOP2_RTE);
                         if ((ln == 3)
                             && ((AO4.type != VARIABLE_OT)||(AO4.value != 0))
                             && ((AO4.type != VARIABLE_OT)
                                 ||(AO4.value != AO.value)))
                         {   assembly_operand en_ao;
                             INITAOTV(&en_ao, SHORT_CONSTANT_OT, OBJECTLOOP_BROKEN_RTE);
                             assemblez_2_branch(jin_zc, AO, AO4,
                                 next_label, TRUE);
                             assemblez_3(call_vn_zc, veneer_routine(RT__Err_VR),
                                 en_ao, AO);
                             assemblez_jump(ln2);
                             assemble_label_no(next_label++);
                         }
                     }
                     else AO2 = AO;
                     assemblez_objcode(get_sibling_zc, AO2, AO, ln4, TRUE);
                     assemble_label_no(ln2);
                     return;
                 }

                 sequence_point_follows = TRUE;
                 INITAOTV(&AO2, SHORT_CONSTANT_OT, 1);
                 assemblez_store(AO, AO2);

                 assemble_label_no(ln = next_label++);
                 ln2 = next_label++;
                 ln3 = next_label++;
                 if (flag)
                 {   put_token_back();
                     put_token_back();
                     sequence_point_follows = TRUE;
                     code_generate(parse_expression(CONDITION_CONTEXT),
                         CONDITION_CONTEXT, ln3);
                     match_close_bracket();
                 }
                 parse_code_block(ln2, ln3, 0);

                 sequence_point_follows = FALSE;
                 assemble_label_no(ln3);
                 assemblez_inc(AO);
                 INITAOTV(&AO2, LONG_CONSTANT_OT, no_objects);
                 AO2.marker = NO_OBJS_MV;
                 assemblez_2_branch(jg_zc, AO, AO2, ln2, TRUE);
                 assemblez_jump(ln);
                 assemble_label_no(ln2);
                 return;

    /*  -------------------------------------------------------------------- */
    /*  (see routine above) ------------------------------------------------ */
    /*  -------------------------------------------------------------------- */

        case PRINT_CODE:
            get_next_token();
            parse_print_z(FALSE); return;
        case PRINT_RET_CODE:
            get_next_token();
            parse_print_z(TRUE); return;

    /*  -------------------------------------------------------------------- */
    /*  quit --------------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case QUIT_CODE:      assemblez_0(quit_zc); break;

    /*  -------------------------------------------------------------------- */
    /*  read <expression> <expression> [<Routine>] ------------------------- */
    /*  -------------------------------------------------------------------- */

        case READ_CODE:
                 INITAOTV(&AO, VARIABLE_OT, 252);
                 assemblez_store(AO,
                     code_generate(parse_expression(QUANTITY_CONTEXT),
                                   QUANTITY_CONTEXT, -1));
                 if (version_number > 3)
                 {   INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
                     INITAOTV(&AO4, SHORT_CONSTANT_OT, 0);
                     assemblez_3(storeb_zc, AO, AO3, AO4);
                 }
                 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                           QUANTITY_CONTEXT, -1);

                 get_next_token();
                 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
                     put_token_back();
                 else
                 {   if (version_number == 3)
                         error(
"In Version 3 no status-line drawing routine can be given");
                     else
                     {   assembly_operand AO5;
                         /* Move the temp4 (buffer) value to the stack,
                            since the routine might alter temp4. */
                         assemblez_store(stack_pointer, AO);
                         AO = stack_pointer;
                         put_token_back();
                         AO5 = parse_expression(CONSTANT_CONTEXT);

                         if (version_number >= 5)
                             assemblez_1(call_1n_zc, AO5);
                         else
                             assemblez_1_to(call_zc, AO5, temp_var1);
                     }
                 }

                 if (version_number > 4)
                 {   assemblez_2_to(aread_zc, AO, AO2, temp_var1);
                 }
                 else assemblez_2(sread_zc, AO, AO2);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  remove <expression> ------------------------------------------------ */
    /*  -------------------------------------------------------------------- */

        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),
                             AO);
                     else
                     {   assemblez_2_to(call_zc, veneer_routine(RT__ChR_VR),
                             AO, temp_var1);
                     }
                 }
                 else
                     assemblez_1(remove_obj_zc, AO);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  restore <label> ---------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case RESTORE_CODE:
                 if (version_number < 5)
                     assemblez_0_branch(restore_zc, parse_label(), TRUE);
                 else
                 {   INITAOTV(&AO2, SHORT_CONSTANT_OT, 2);
                     assemblez_0_to(restore_zc, temp_var1);
                     assemblez_2_branch(je_zc, temp_var1, AO2, parse_label(), TRUE);
                 }
                 break;

    /*  -------------------------------------------------------------------- */
    /*  return [<expression>] ---------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case RETURN_CODE:
                 get_next_token();
                 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
                 {   assemblez_0(rtrue_zc); return; }
                 put_token_back();
                 AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
                     QUANTITY_CONTEXT, -1);
                 if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 0)
                     && (AO.marker == 0))
                 {   assemblez_0(rfalse_zc); break; }
                 if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 1)
                     && (AO.marker == 0))
                 {   assemblez_0(rtrue_zc); break; }
                 if ((AO.type == VARIABLE_OT) && (AO.value == 0))
                 {   assemblez_0(ret_popped_zc); break; }
                 assemblez_1(ret_zc, AO);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  rfalse ------------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case RFALSE_CODE:  assemblez_0(rfalse_zc); break;

    /*  -------------------------------------------------------------------- */
    /*  rtrue -------------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case RTRUE_CODE:   assemblez_0(rtrue_zc); break;

    /*  -------------------------------------------------------------------- */
    /*  save <label> ------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case SAVE_CODE:
                 if (version_number < 5)
                     assemblez_0_branch(save_zc, parse_label(), TRUE);
                 else
                 {   INITAOTV(&AO, VARIABLE_OT, 255);
                     assemblez_0_to(save_zc, AO);
                     assemblez_1_branch(jz_zc, AO, parse_label(), FALSE);
                 }
                 break;

    /*  -------------------------------------------------------------------- */
    /*  spaces <expression> ------------------------------------------------ */
    /*  -------------------------------------------------------------------- */

        case SPACES_CODE:
                 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                     QUANTITY_CONTEXT, -1);
                 INITAOTV(&AO2, VARIABLE_OT, 255);

                 assemblez_store(AO2, AO);

                 INITAOTV(&AO, SHORT_CONSTANT_OT, 32);
                 INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);

                 assemblez_2_branch(jl_zc, AO2, AO3, ln = next_label++, TRUE);
                 assemble_label_no(ln2 = next_label++);
                 assemblez_1(print_char_zc, AO);
                 assemblez_dec(AO2);
                 assemblez_1_branch(jz_zc, AO2, ln2, FALSE);
                 assemble_label_no(ln);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  string <expression> <literal-string> ------------------------------- */
    /*  -------------------------------------------------------------------- */

        case STRING_CODE:
                 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
                 INITAOTV(&AO2, SHORT_CONSTANT_OT, 12);
                 INITAOTV(&AO3, VARIABLE_OT, 252);
                 assemblez_2_to(loadw_zc, AO, AO2, AO3);
                 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 >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
                         error_max_dynamic_strings(AO2.value);
                         AO2.value = 0;
                     }
                 }
                 get_next_token();
                 if (token_type == DQ_TT)
                 {   INITAOT(&AO4, LONG_CONSTANT_OT);
                     /* 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();
                     AO4 = parse_expression(CONSTANT_CONTEXT);
                 }
                 assemblez_3(storew_zc, AO3, AO2, AO4);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  style roman/reverse/bold/underline/fixed --------------------------- */
    /*  -------------------------------------------------------------------- */

        case STYLE_CODE:
                 if (version_number==3)
                 {   error(
"The 'style' statement cannot be used for Version 3 games");
                     panic_mode_error_recovery();
                     break;
                 }

                 misc_keywords.enabled = TRUE;
                 get_next_token();
                 misc_keywords.enabled = FALSE;
                 if ((token_type != MISC_KEYWORD_TT)
                     || ((token_value != ROMAN_MK)
                         && (token_value != REVERSE_MK)
                         && (token_value != BOLD_MK)
                         && (token_value != UNDERLINE_MK)
                         && (token_value != FIXED_MK)))
                 {   ebf_error(
"'roman', 'bold', 'underline', 'reverse' or 'fixed'",
                         token_text);
                     panic_mode_error_recovery();
                     break;
                 }

                 INITAOT(&AO, SHORT_CONSTANT_OT);
                 switch(token_value)
                 {   case ROMAN_MK: AO.value = 0; break;
                     case REVERSE_MK: AO.value = 1; break;
                     case BOLD_MK: AO.value = 2; break;
                     case UNDERLINE_MK: AO.value = 4; break;
                     case FIXED_MK: AO.value = 8; break;
                 }
                 assemblez_1(set_text_style_zc, AO); break;

    /*  -------------------------------------------------------------------- */
    /*  switch (<expression>) <codeblock> ---------------------------------- */
    /*  -------------------------------------------------------------------- */

        case SWITCH_CODE:
                 match_open_bracket();
                 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                     QUANTITY_CONTEXT, -1);
                 match_close_bracket();

                 INITAOTV(&AO2, VARIABLE_OT, 255);
                 assemblez_store(AO2, AO);

                 parse_code_block(ln = next_label++, continue_label, 1);
                 assemble_forward_label_no(ln);
                 return;

    /*  -------------------------------------------------------------------- */
    /*  while (<condition>) <codeblock> ------------------------------------ */
    /*  -------------------------------------------------------------------- */

        case WHILE_CODE:
                 assemble_label_no(ln = next_label++);
                 match_open_bracket();

                 code_generate(parse_expression(CONDITION_CONTEXT),
                     CONDITION_CONTEXT, ln2 = next_label++);
                 match_close_bracket();

                 parse_code_block(ln2, ln, 0);
                 sequence_point_follows = FALSE;
                 assemblez_jump(ln);
                 assemble_forward_label_no(ln2);
                 return;

    /*  -------------------------------------------------------------------- */

        case SDEFAULT_CODE:
                 error("'default' without matching 'switch'"); break;
        case ELSE_CODE:
                 error("'else' without matching 'if'"); break;
        case UNTIL_CODE:
                 error("'until' without matching 'do'");
                 panic_mode_error_recovery(); return;
    }

    StatementTerminator:

    get_next_token();
    if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
    {   ebf_error("';'", token_text);
        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 == HASH_SEP))
    {   parse_directive(TRUE);
        parse_statement(break_label, continue_label); return;
    }

    if ((token_type == SEP_TT) && (token_value == AT_SEP))
    {   parse_assembly(); return;
    }

    if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;

    if (token_type == DQ_TT)
    {   parse_print_g(TRUE); return;
    }

    if ((token_type == SEP_TT) && (token_value == LESS_SEP))
    {   parse_action(); goto StatementTerminator; }

    if (token_type == EOF_TT)
    {   ebf_error("statement", token_text); return; }

    if (token_type != STATEMENT_TT)
    {   put_token_back();
        AO = parse_expression(VOID_CONTEXT);
        code_generate(AO, VOID_CONTEXT, -1);
        if (vivc_flag) { panic_mode_error_recovery(); return; }
        goto StatementTerminator;
    }

    statements.enabled = FALSE;

    switch(token_value)
    {

    /*  -------------------------------------------------------------------- */
    /*  box <string-1> ... <string-n> -------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case BOX_CODE:
            INITAOT(&AO3, CONSTANT_OT);
                 AO3.value = begin_table_array();
                 AO3.marker = ARRAY_MV;
                 ln = 0; ln2 = 0;
                 do
                 {   get_next_token();
                     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);
                     {   int i, j;
                         for (i=0, j=0; token_text[i] != 0; j++)
                             if (token_text[i] == '@')
                             {   if (token_text[i+1] == '@')
                                 {   i = i + 2;
                                     while (isdigit(token_text[i])) i++;
                                 }
                                 else
                                 {   i++;
                                     if (token_text[i] != 0) i++;
                                     if (token_text[i] != 0) i++;
                                 }
                             }
                             else i++;
                         if (j > ln2) ln2 = j;
                     }
                     put_token_back();
                     array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
                 } while (TRUE);
                 finish_array(ln, FALSE);
                 if (ln == 0)
                     error("No lines of text given for 'box' display");

                 INITAO(&AO2);
                 AO2.value = ln2; set_constant_ot(&AO2);
                 assembleg_call_2(veneer_routine(Box__Routine_VR),
                     AO2, AO3, zero_operand);
                 return;

    /*  -------------------------------------------------------------------- */
    /*  break -------------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case BREAK_CODE:
                 if (break_label == -1)
                 error("'break' can only be used in a loop or 'switch' block");
                 else
                     assembleg_jump(break_label);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  continue ----------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case CONTINUE_CODE:
                 if (continue_label == -1)
                 error("'continue' can only be used in a loop block");
                 else
                     assembleg_jump(continue_label);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  do <codeblock> until (<condition>) --------------------------------- */
    /*  -------------------------------------------------------------------- */

        case DO_CODE:
                 assemble_label_no(ln = next_label++);
                 ln2 = next_label++; ln3 = next_label++;
                 parse_code_block(ln3, ln2, 0);
                 statements.enabled = TRUE;
                 get_next_token();
                 if ((token_type == STATEMENT_TT)
                     && (token_value == UNTIL_CODE))
                 {   assemble_forward_label_no(ln2);
                     match_open_bracket();
                     AO = parse_expression(CONDITION_CONTEXT);
                     match_close_bracket();
                     code_generate(AO, CONDITION_CONTEXT, ln);
                 }
                 else error("'do' without matching 'until'");

                 assemble_forward_label_no(ln3);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  font on/off -------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case FONT_CODE:
                 misc_keywords.enabled = TRUE;
                 get_next_token();
                 misc_keywords.enabled = FALSE;
                 if ((token_type != MISC_KEYWORD_TT)
                     || ((token_value != ON_MK)
                         && (token_value != OFF_MK)))
                 {   ebf_error("'on' or 'off'", token_text);
                     panic_mode_error_recovery();
                     break;
                 }

                 /* Call glk_set_style(normal or preformatted) */
                 INITAO(&AO);
                 AO.value = 0x0086;
                 set_constant_ot(&AO);
                 if (token_value == ON_MK)
                   AO2 = zero_operand;
                 else 
                   AO2 = two_operand;
                 assembleg_call_2(veneer_routine(Glk__Wrap_VR), 
                   AO, AO2, zero_operand);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  for (<initialisation> : <continue-condition> : <updating>) --------- */
    /*  -------------------------------------------------------------------- */

        /*  Note that it's legal for any or all of the three sections of a
            'for' specification to be empty.  This 'for' implementation
            often wastes 3 bytes with a redundant branch rather than keep
            expression parse trees for long periods (as previous versions
            of Inform did, somewhat crudely by simply storing the textual
            form of a 'for' loop).  It is adequate for now.                  */

        case FOR_CODE:
                 match_open_bracket();
                 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();
                     if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
                     {   sequence_point_follows = TRUE;
                         statement_debug_location = get_token_location();
                         code_generate(parse_expression(FORINIT_CONTEXT),
                             VOID_CONTEXT, -1);
                     }
                     get_next_token();
                     if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
                     {   get_next_token();
                         if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
                         {   assemble_label_no(ln = next_label++);
                             ln2 = next_label++;
                             parse_code_block(ln2, ln, 0);
                             sequence_point_follows = FALSE;
                             if (!execution_never_reaches_here)
                                 assembleg_jump(ln);
                             assemble_forward_label_no(ln2);
                             return;
                         }
                         goto ParseUpdate;
                     }
                     put_token_back();
                     if (!match_colon()) break;
                 }

                 get_next_token();
                 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
                 {   put_token_back();
                     spare_debug_location1 = get_token_location();
                     AO = parse_expression(CONDITION_CONTEXT);
                     if (!match_colon()) break;
                 }
                 get_next_token();

                 ParseUpdate:
                 if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
                 {   put_token_back();
                     spare_debug_location2 = get_token_location();
                     AO2 = parse_expression(VOID_CONTEXT);
                     match_close_bracket();
                     flag = test_for_incdec(AO2);
                 }

                 ln = next_label++;
                 ln2 = next_label++;
                 ln3 = next_label++;

                 if ((AO2.type == OMITTED_OT) || (flag != 0))
                 {
                     assemble_label_no(ln);
                     if (flag==0) assemble_label_no(ln2);

                     /*  The "finished yet?" condition  */

                     if (AO.type != OMITTED_OT)
                     {   sequence_point_follows = TRUE;
                         statement_debug_location = spare_debug_location1;
                         code_generate(AO, CONDITION_CONTEXT, ln3);
                     }

                 }
                 else
                 {
                     /*  This is the jump which could be avoided with the aid
                         of long-term expression storage  */

                     sequence_point_follows = FALSE;
                     assembleg_jump(ln2);

                     /*  The "update" part  */

                     assemble_label_no(ln);
                     sequence_point_follows = TRUE;
                     statement_debug_location = spare_debug_location2;
                     code_generate(AO2, VOID_CONTEXT, -1);

                     assemble_label_no(ln2);

                     /*  The "finished yet?" condition  */

                     if (AO.type != OMITTED_OT)
                     {   sequence_point_follows = TRUE;
                         statement_debug_location = spare_debug_location1;
                         code_generate(AO, CONDITION_CONTEXT, ln3);
                     }
                 }

                 if (flag != 0)
                 {
                     /*  In this optimised case, update code is at the end
                         of the loop block, so "continue" goes there  */

                     parse_code_block(ln3, ln2, 0);
                     assemble_label_no(ln2);

                     sequence_point_follows = TRUE;
                     statement_debug_location = spare_debug_location2;
                     if (flag > 0)
                     {   INITAO(&AO3);
                         AO3.value = flag;
                         if (AO3.value >= MAX_LOCAL_VARIABLES)
                           AO3.type = GLOBALVAR_OT;
                         else
                           AO3.type = LOCALVAR_OT;
                         assembleg_3(add_gc, AO3, one_operand, AO3);
                     }
                     else
                     {   INITAO(&AO3);
                         AO3.value = -flag;
                         if (AO3.value >= MAX_LOCAL_VARIABLES)
                           AO3.type = GLOBALVAR_OT;
                         else
                           AO3.type = LOCALVAR_OT;
                         assembleg_3(sub_gc, AO3, one_operand, AO3);
                     }
                     assembleg_jump(ln);
                 }
                 else
                 {
                     /*  In the unoptimised case, update code is at the
                         start of the loop block, so "continue" goes there  */

                     parse_code_block(ln3, ln, 0);
                     if (!execution_never_reaches_here)
                     {   sequence_point_follows = FALSE;
                         assembleg_jump(ln);
                     }
                 }

                 assemble_forward_label_no(ln3);
                 return;

    /*  -------------------------------------------------------------------- */
    /*  give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
    /*  -------------------------------------------------------------------- */

        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
                     onstack = FALSE;

                 do
                 {   get_next_token();
                     if ((token_type == SEP_TT) 
                       && (token_value == SEMICOLON_SEP)) {
                         if (onstack) {
                           assembleg_2(copy_gc, stack_pointer, zero_operand);
                         }
                         return;
                     }
                     if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
                         ln = 0;
                     else
                     {   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)) {
                           /* already on stack */
                         }
                         else {
                           assembleg_store(stack_pointer, AO2);
                         }
                         if (onstack)
                           assembleg_2(stkpeek_gc, one_operand, stack_pointer);
                         else
                           assembleg_store(stack_pointer, AO);
                         assembleg_3(call_gc, veneer_routine(ln2), two_operand,
                           zero_operand);
                     }
                     else {
                         if (is_constant_ot(AO2.type) && AO2.marker == 0) {
                           AO2.value += 8;
                           set_constant_ot(&AO2);
                         }
                         else {
                           INITAOTV(&AO3, BYTECONSTANT_OT, 8);
                           assembleg_3(add_gc, AO2, AO3, stack_pointer);
                           AO2 = stack_pointer;
                         }
                         if (onstack) {
                           if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0))
                             assembleg_2(stkpeek_gc, one_operand, 
                               stack_pointer);
                           else
                             assembleg_2(stkpeek_gc, zero_operand, 
                               stack_pointer);
                         }
                         if (ln) 
                           AO3 = one_operand;
                         else
                           AO3 = zero_operand;
                         assembleg_3(astorebit_gc, AO, AO2, AO3);
                     }
                 } while(TRUE);

    /*  -------------------------------------------------------------------- */
    /*  if (<condition>) <codeblock> [else <codeblock>] -------------------- */
    /*  -------------------------------------------------------------------- */

        case IF_CODE:
                 flag = FALSE; /* set if there's an "else" */
                 ln2 = 0;
                 pre_unreach = execution_never_reaches_here;

                 match_open_bracket();
                 AO = parse_expression(CONDITION_CONTEXT);
                 match_close_bracket();

                 statements.enabled = TRUE;
                 get_next_token();
                 if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
                     ln = -4;
                 else
                 if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
                     ln = -3;
                 else
                 {   put_token_back();
                     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);
                         put_token_back();
                     }
                 }

                 statements.enabled = TRUE;
                 get_next_token();
                 
                 /* An #if directive around the ELSE clause is legal. */
                 while ((token_type == SEP_TT) && (token_value == HASH_SEP))
                 {   parse_directive(TRUE);
                     statements.enabled = TRUE;
                     get_next_token();
                 }
                 
                 if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
                 {   flag = TRUE;
                     if (ln >= 0)
                     {   ln2 = next_label++;
                         if (!execution_never_reaches_here)
                         {   sequence_point_follows = FALSE;
                             assembleg_jump(ln2);
                         }
                     }
                 }
                 else put_token_back();

                 /* 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)
                 {
                     /* 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;

    /*  -------------------------------------------------------------------- */
    /*  inversion ---------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case INVERSION_CODE:
                 INITAOTV(&AO2, DEREFERENCE_OT, GLULX_HEADER_SIZE+8);
                 assembleg_2(copyb_gc, AO2, stack_pointer);
                 assembleg_1(streamchar_gc, stack_pointer);
                 AO2.value  = GLULX_HEADER_SIZE+9; 
                 assembleg_2(copyb_gc, AO2, stack_pointer);
                 assembleg_1(streamchar_gc, stack_pointer);
                 AO2.value  = GLULX_HEADER_SIZE+10; 
                 assembleg_2(copyb_gc, AO2, stack_pointer);
                 assembleg_1(streamchar_gc, stack_pointer);
                 AO2.value  = GLULX_HEADER_SIZE+11; 
                 assembleg_2(copyb_gc, AO2, stack_pointer);
                 assembleg_1(streamchar_gc, stack_pointer);

                 if (/* DISABLES CODE */ (0)) {
                     INITAO(&AO);
                     AO.value = '(';
                     set_constant_ot(&AO);
                     assembleg_1(streamchar_gc, AO);
                     AO.value = 'G';
                     set_constant_ot(&AO);
                     assembleg_1(streamchar_gc, AO);

                     AO2.value  = GLULX_HEADER_SIZE+12; 
                     assembleg_2(copyb_gc, AO2, stack_pointer);
                     assembleg_1(streamchar_gc, stack_pointer);
                     AO2.value  = GLULX_HEADER_SIZE+13; 
                     assembleg_2(copyb_gc, AO2, stack_pointer);
                     assembleg_1(streamchar_gc, stack_pointer);
                     AO2.value  = GLULX_HEADER_SIZE+14; 
                     assembleg_2(copyb_gc, AO2, stack_pointer);
                     assembleg_1(streamchar_gc, stack_pointer);
                     AO2.value  = GLULX_HEADER_SIZE+15; 
                     assembleg_2(copyb_gc, AO2, stack_pointer);
                     assembleg_1(streamchar_gc, stack_pointer);

                     AO.marker = 0;
                     AO.value = ')';
                     set_constant_ot(&AO);
                     assembleg_1(streamchar_gc, AO);
                 }

                 break;

    /*  -------------------------------------------------------------------- */
    /*  jump <label> ------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case JUMP_CODE:
                 assembleg_jump(parse_label());
                 break;

    /*  -------------------------------------------------------------------- */
    /*  move <expression> to <expression> ---------------------------------- */
    /*  -------------------------------------------------------------------- */

        case MOVE_CODE:
                 misc_keywords.enabled = TRUE;
                 AO = parse_expression(QUANTITY_CONTEXT);

                 get_next_token();
                 misc_keywords.enabled = FALSE;
                 if ((token_type != MISC_KEYWORD_TT)
                     || (token_value != TO_MK))
                 {   ebf_error("'to'", token_text);
                     panic_mode_error_recovery();
                     return;
                 }

                 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);
                 else
                     assembleg_call_2(veneer_routine(OB__Move_VR), AO, AO2,
                         zero_operand);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  new_line ----------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case NEW_LINE_CODE:  
              INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
              assembleg_1(streamchar_gc, AO); 
              break;

    /*  -------------------------------------------------------------------- */
    /*  objectloop (<initialisation>) <codeblock> -------------------------- */
    /*  -------------------------------------------------------------------- */

        case OBJECTLOOP_CODE:

                 match_open_bracket();
                 get_next_token();
                 if (token_type == LOCAL_VARIABLE_TT) {
                     INITAOTV(&AO, LOCALVAR_OT, token_value);
                 }
                 else if ((token_type == SYMBOL_TT) &&
                   (symbols[token_value].type == GLOBAL_VARIABLE_T)) {
                     INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
                 }
                 else {
                     ebf_error("'objectloop' variable", token_text);
                     panic_mode_error_recovery(); 
                     break;
                 }
                 misc_keywords.enabled = TRUE;
                 get_next_token(); flag = TRUE;
                 misc_keywords.enabled = FALSE;
                 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
                     flag = FALSE;

                 ln = 0;
                 if ((token_type == MISC_KEYWORD_TT)
                     && (token_value == NEAR_MK)) ln = 1;
                 if ((token_type == MISC_KEYWORD_TT)
                     && (token_value == FROM_MK)) ln = 2;
                 if ((token_type == CND_TT) && (token_value == IN_COND))
                 {   get_next_token();
                     get_next_token();
                     if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
                         ln = 3;
                     put_token_back();
                     put_token_back();
                 }

                 if (ln != 0) {
                   /*  Old style (Inform 5) objectloops: note that we
                       implement objectloop (a in b) in the old way since
                       this runs through objects in a different order from
                       the new way, and there may be existing Inform code
                       relying on this.                                    */
                     assembly_operand AO4, AO5;
                     INITAO(&AO5);

                     sequence_point_follows = TRUE;
                     AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                         QUANTITY_CONTEXT, -1);
                     match_close_bracket();
                     if (ln == 1) {
                         if (runtime_error_checking_switch)
                             AO2 = check_nonzero_at_runtime(AO2, -1,
                                 OBJECTLOOP_RTE);
                         INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
                         assembleg_3(aload_gc, AO2, AO4, stack_pointer);
                         INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
                         assembleg_3(aload_gc, stack_pointer, AO4, stack_pointer);
                         AO2 = stack_pointer;
                     }
                     else if (ln == 3) {
                         if (runtime_error_checking_switch) {
                             AO5 = AO2;
                             AO2 = check_nonzero_at_runtime(AO2, -1,
                                 CHILD_RTE);
                         }
                         INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
                         assembleg_3(aload_gc, AO2, AO4, stack_pointer);
                         AO2 = stack_pointer;
                     }
                     else {
                         /* do nothing */
                     }
                     assembleg_store(AO, AO2);
                     assembleg_1_branch(jz_gc, AO, ln2 = next_label++);
                     assemble_label_no(ln4 = next_label++);
                     parse_code_block(ln2, ln3 = next_label++, 0);
                     sequence_point_follows = FALSE;
                     assemble_label_no(ln3);
                     if (runtime_error_checking_switch) {
                         AO2 = check_nonzero_at_runtime(AO, ln2,
                              OBJECTLOOP2_RTE);
                         if ((ln == 3)
                             && ((AO5.type != LOCALVAR_OT)||(AO5.value != 0))
                             && ((AO5.type != LOCALVAR_OT)||(AO5.value != AO.value)))
                         {   assembly_operand en_ao;
                             INITAO(&en_ao);
                             en_ao.value = OBJECTLOOP_BROKEN_RTE;
                             set_constant_ot(&en_ao);
                             INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
                             assembleg_3(aload_gc, AO, AO4, stack_pointer);
                             assembleg_2_branch(jeq_gc, stack_pointer, AO5, 
                                 next_label);
                             assembleg_call_2(veneer_routine(RT__Err_VR),
                                 en_ao, AO, zero_operand);
                             assembleg_jump(ln2);
                             assemble_label_no(next_label++);
                         }
                     }
                     else {
                         AO2 = AO;
                     }
                     INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_SIBLING());
                     assembleg_3(aload_gc, AO2, AO4, AO);
                     assembleg_1_branch(jnz_gc, AO, ln4);
                     assemble_label_no(ln2);
                     return;
                 }

                 sequence_point_follows = TRUE;
                 ln = symbol_index("Class", -1);
                 INITAOT(&AO2, CONSTANT_OT);
                 AO2.value = symbols[ln].value;
                 AO2.marker = OBJECT_MV;
                 assembleg_store(AO, AO2);

                 assemble_label_no(ln = next_label++);
                 ln2 = next_label++;
                 ln3 = next_label++;
                 if (flag)
                 {   put_token_back();
                     put_token_back();
                     sequence_point_follows = TRUE;
                     code_generate(parse_expression(CONDITION_CONTEXT),
                         CONDITION_CONTEXT, ln3);
                     match_close_bracket();
                 }
                 parse_code_block(ln2, ln3, 0);

                 sequence_point_follows = FALSE;
                 assemble_label_no(ln3);
                 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHAIN());
                 assembleg_3(aload_gc, AO, AO4, AO);
                 assembleg_1_branch(jnz_gc, AO, ln);
                 assemble_label_no(ln2);
                 return;

    /*  -------------------------------------------------------------------- */
    /*  (see routine above) ------------------------------------------------ */
    /*  -------------------------------------------------------------------- */

        case PRINT_CODE:
            get_next_token();
            parse_print_g(FALSE); return;
        case PRINT_RET_CODE:
            get_next_token();
            parse_print_g(TRUE); return;

    /*  -------------------------------------------------------------------- */
    /*  quit --------------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case QUIT_CODE:
                 assembleg_0(quit_gc); break;

    /*  -------------------------------------------------------------------- */
    /*  remove <expression> ------------------------------------------------ */
    /*  -------------------------------------------------------------------- */

        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);
                 else
                     assembleg_call_1(veneer_routine(OB__Remove_VR), AO,
                         zero_operand);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  return [<expression>] ---------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case RETURN_CODE:
          get_next_token();
          if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) {
            assembleg_1(return_gc, one_operand); 
            return; 
          }
          put_token_back();
          AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
            QUANTITY_CONTEXT, -1);
          assembleg_1(return_gc, AO);
          break;

    /*  -------------------------------------------------------------------- */
    /*  rfalse ------------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case RFALSE_CODE:   
          assembleg_1(return_gc, zero_operand); 
          break;

    /*  -------------------------------------------------------------------- */
    /*  rtrue -------------------------------------------------------------- */
    /*  -------------------------------------------------------------------- */

        case RTRUE_CODE:   
          assembleg_1(return_gc, one_operand); 
          break;

    /*  -------------------------------------------------------------------- */
    /*  spaces <expression> ------------------------------------------------ */
    /*  -------------------------------------------------------------------- */

        case SPACES_CODE:
                 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                     QUANTITY_CONTEXT, -1);

                 assembleg_store(temp_var1, AO);

                 INITAO(&AO);
                 AO.value = 32; set_constant_ot(&AO);

                 assembleg_2_branch(jlt_gc, temp_var1, one_operand, 
                     ln = next_label++);
                 assemble_label_no(ln2 = next_label++);
                 assembleg_1(streamchar_gc, AO);
                 assembleg_dec(temp_var1);
                 assembleg_1_branch(jnz_gc, temp_var1, ln2);
                 assemble_label_no(ln);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  string <expression> <literal-string> ------------------------------- */
    /*  -------------------------------------------------------------------- */

        case STRING_CODE:
                 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) {
                         error_max_dynamic_strings(AO2.value);
                     }
                 }
                 get_next_token();
                 if (token_type == DQ_TT)
                 {   INITAOT(&AO4, CONSTANT_OT);
                     /* 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
                 {   put_token_back();
                     AO4 = parse_expression(CONSTANT_CONTEXT);
                 }
                 assembleg_call_2(veneer_routine(Dynam__String_VR),
                   AO2, AO4, zero_operand);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  style roman/reverse/bold/underline/fixed --------------------------- */
    /*  -------------------------------------------------------------------- */

        case STYLE_CODE:
                 misc_keywords.enabled = TRUE;
                 get_next_token();
                 misc_keywords.enabled = FALSE;
                 if ((token_type != MISC_KEYWORD_TT)
                     || ((token_value != ROMAN_MK)
                         && (token_value != REVERSE_MK)
                         && (token_value != BOLD_MK)
                         && (token_value != UNDERLINE_MK)
                         && (token_value != FIXED_MK)))
                 {   ebf_error(
"'roman', 'bold', 'underline', 'reverse' or 'fixed'",
                         token_text);
                     panic_mode_error_recovery();
                     break;
                 }

                 /* Call glk_set_style() */

                 INITAO(&AO);
                 AO.value = 0x0086;
                 set_constant_ot(&AO);
                 switch(token_value)
                 {   case ROMAN_MK:
                     default: 
                         AO2 = zero_operand; /* normal */
                         break;
                     case REVERSE_MK: 
                         INITAO(&AO2);
                         AO2.value = 5; /* alert */
                         set_constant_ot(&AO2);
                         break;
                     case BOLD_MK: 
                         INITAO(&AO2);
                         AO2.value = 4; /* subheader */
                         set_constant_ot(&AO2);
                         break;
                     case UNDERLINE_MK: 
                         AO2 = one_operand; /* emphasized */
                         break;
                     case FIXED_MK: 
                         AO2 = two_operand; /* preformatted */
                         break;
                 }
                 assembleg_call_2(veneer_routine(Glk__Wrap_VR), 
                   AO, AO2, zero_operand);
                 break;

    /*  -------------------------------------------------------------------- */
    /*  switch (<expression>) <codeblock> ---------------------------------- */
    /*  -------------------------------------------------------------------- */

        case SWITCH_CODE:
                 match_open_bracket();
                 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                     QUANTITY_CONTEXT, -1);
                 match_close_bracket();

                 assembleg_store(temp_var1, AO); 

                 parse_code_block(ln = next_label++, continue_label, 1);
                 assemble_forward_label_no(ln);
                 return;

    /*  -------------------------------------------------------------------- */
    /*  while (<condition>) <codeblock> ------------------------------------ */
    /*  -------------------------------------------------------------------- */

        case WHILE_CODE:
                 assemble_label_no(ln = next_label++);
                 match_open_bracket();

                 code_generate(parse_expression(CONDITION_CONTEXT),
                     CONDITION_CONTEXT, ln2 = next_label++);
                 match_close_bracket();

                 parse_code_block(ln2, ln, 0);
                 sequence_point_follows = FALSE;
                 assembleg_jump(ln);
                 assemble_forward_label_no(ln2);
                 return;

    /*  -------------------------------------------------------------------- */

        case SDEFAULT_CODE:
                 error("'default' without matching 'switch'"); break;
        case ELSE_CODE:
                 error("'else' without matching 'if'"); break;
        case UNTIL_CODE:
                 error("'until' without matching 'do'");
                 panic_mode_error_recovery(); return;

    /*  -------------------------------------------------------------------- */

    /* And a useful default, which will never be triggered in a complete
       Inform compiler, but which is important in development. */

        default:
          error("*** Statement code gen: Can't generate yet ***\n");
          panic_mode_error_recovery(); return;
    }

    StatementTerminator:

    get_next_token();
    if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
    {   ebf_error("';'", token_text);
        put_token_back();
    }
}

extern void parse_statement(int break_label, int 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;
}

/* ========================================================================= */
/*   Data structure management routines                                      */
/* ------------------------------------------------------------------------- */

extern void init_states_vars(void)
{
}

extern void states_begin_pass(void)
{
}

extern void states_allocate_arrays(void)
{
}

extern void states_free_arrays(void)
{
}

/* ========================================================================= */
