--- /dev/null
+/* ------------------------------------------------------------------------- */
+/* "states" : Statement translator */
+/* */
+/* Copyright (c) Graham Nelson 1993 - 2018 */
+/* */
+/* This file is part of Inform. */
+/* */
+/* Inform is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 3 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Inform is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Inform. If not, see https://gnu.org/licenses/ */
+/* */
+/* ------------------------------------------------------------------------- */
+
+#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(call_zc, AO, AO2, AO3, AO4);
+ 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) &&
+ (stypes[token_value] == LABEL_T))
+ { sflags[token_value] |= USED_SFLAG;
+ return(svals[token_value]);
+ }
+
+ if ((token_type == SYMBOL_TT) && (sflags[token_value] & UNKNOWN_SFLAG))
+ { assign_symbol(token_value, next_label, LABEL_T);
+ define_symbol_label(token_value);
+ next_label++;
+ sflags[token_value] |= CHANGE_SFLAG + USED_SFLAG;
+ return(svals[token_value]);
+ }
+
+ 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, FALSE, FALSE);
+ 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 (sflags[token_value] & UNKNOWN_SFLAG)
+ { INITAOT(&AO, LONG_CONSTANT_OT);
+ AO.value = token_value;
+ AO.marker = SYMBOL_MV;
+ }
+ else
+ { INITAOT(&AO, LONG_CONSTANT_OT);
+ AO.value = svals[token_value];
+ AO.marker = IROUTINE_MV;
+ if (stypes[token_value] != ROUTINE_T)
+ ebf_error("printing routine name", token_text);
+ }
+ sflags[token_value] |= 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, FALSE, FALSE);
+ 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 (sflags[token_value] & UNKNOWN_SFLAG)
+ { INITAOT(&AO, CONSTANT_OT);
+ AO.value = token_value;
+ AO.marker = SYMBOL_MV;
+ }
+ else
+ { INITAOT(&AO, CONSTANT_OT);
+ AO.value = svals[token_value];
+ AO.marker = IROUTINE_MV;
+ if (stypes[token_value] != ROUTINE_T)
+ ebf_error("printing routine name", token_text);
+ }
+ sflags[token_value] |= 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);
+ }
+}
+
+static void parse_statement_z(int break_label, int continue_label)
+{ int ln, ln2, ln3, ln4, flag;
+ assembly_operand AO, AO2, AO3, AO4;
+ debug_location spare_debug_location1, spare_debug_location2;
+
+ ASSERT_ZCODE();
+
+ if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
+ { /* That is, a full stop, signifying a label */
+
+ get_next_token();
+ if (token_type == SYMBOL_TT)
+ {
+ if (sflags[token_value] & UNKNOWN_SFLAG)
+ { assign_symbol(token_value, next_label, LABEL_T);
+ sflags[token_value] |= USED_SFLAG;
+ assemble_label_no(next_label);
+ define_symbol_label(token_value);
+ next_label++;
+ }
+ else
+ { if (stypes[token_value] != LABEL_T) goto LabelError;
+ if (sflags[token_value] & CHANGE_SFLAG)
+ { sflags[token_value] &= (~(CHANGE_SFLAG));
+ assemble_label_no(svals[token_value]);
+ define_symbol_label(token_value);
+ }
+ else error_named("Duplicate definition of label:", token_text);
+ }
+
+ get_next_token();
+ if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
+ { ebf_error("';'", token_text);
+ put_token_back(); return;
+ }
+
+ /* Interesting point of Inform grammar: a statement can only
+ consist solely of a label when it is immediately followed
+ by a "}". */
+
+ get_next_token();
+ if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
+ { put_token_back(); return;
+ }
+ statement_debug_location = get_token_location();
+ parse_statement(break_label, continue_label);
+ return;
+ }
+ LabelError: ebf_error("label name", token_text);
+ }
+
+ if ((token_type == SEP_TT) && (token_value == HASH_SEP))
+ { parse_directive(TRUE);
+ parse_statement(break_label, continue_label); return;
+ }
+
+ 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++,parse_expression(CONSTANT_CONTEXT));
+ } while (TRUE);
+ finish_array(ln);
+ 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_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_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 */
+
+ 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_label_no(ln2);
+ return;
+ }
+ AO.type = OMITTED_OT;
+ goto ParseUpdate;
+ }
+ put_token_back();
+ if (!match_colon()) break;
+ }
+
+ get_next_token();
+ AO.type = OMITTED_OT;
+ if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
+ { put_token_back();
+ spare_debug_location1 = get_token_location();
+ AO = parse_expression(CONDITION_CONTEXT);
+ if (!match_colon()) break;
+ }
+ get_next_token();
+
+ ParseUpdate:
+ AO2.type = OMITTED_OT; flag = 0;
+ if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
+ { put_token_back();
+ spare_debug_location2 = get_token_location();
+ 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);
+ if (module_switch
+ && (flag>=MAX_LOCAL_VARIABLES) && (flag<LOWEST_SYSTEM_VAR_NUMBER))
+ AO3.marker = VARIABLE_MV;
+ assemblez_1(inc_zc, AO3);
+ }
+ else
+ { INITAOTV(&AO3, SHORT_CONSTANT_OT, -flag);
+ if ((module_switch) && (flag>=MAX_LOCAL_VARIABLES)
+ && (flag<LOWEST_SYSTEM_VAR_NUMBER))
+ AO3.marker = VARIABLE_MV;
+ assemblez_1(dec_zc, AO3);
+ }
+ assemblez_jump(ln);
+ }
+ 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_label_no(ln3);
+ return;
+
+ /* -------------------------------------------------------------------- */
+ /* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
+ /* -------------------------------------------------------------------- */
+
+ case GIVE_CODE:
+ AO = code_generate(parse_expression(QUANTITY_CONTEXT),
+ QUANTITY_CONTEXT, -1);
+ 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
+ { if ((token_type == SYMBOL_TT)
+ && (stypes[token_value] != ATTRIBUTE_T))
+ warning_named("This is not a declared Attribute:",
+ token_text);
+ ln = set_attr_zc;
+ put_token_back();
+ }
+ AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
+ QUANTITY_CONTEXT, -1);
+ 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;
+ ln2 = 0;
+
+ 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++;
+ }
+
+ code_generate(AO, CONDITION_CONTEXT, ln);
+
+ 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();
+ 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();
+
+ if (ln >= 0) assemble_label_no(ln);
+
+ if (flag)
+ { parse_code_block(break_label, continue_label, 0);
+ if (ln >= 0) assemble_label_no(ln2);
+ }
+
+ 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);
+ 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) &&
+ (stypes[token_value] == GLOBAL_VARIABLE_T))
+ AO.value = svals[token_value];
+ else
+ { ebf_error("'objectloop' variable", token_text);
+ panic_mode_error_recovery(); break;
+ }
+ if ((module_switch) && (AO.value >= MAX_LOCAL_VARIABLES)
+ && (AO.value < LOWEST_SYSTEM_VAR_NUMBER))
+ AO.marker = VARIABLE_MV;
+ misc_keywords.enabled = TRUE;
+ get_next_token(); flag = TRUE;
+ misc_keywords.enabled = FALSE;
+ 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);
+ 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);
+ get_next_token();
+ if (token_type == DQ_TT)
+ { INITAOT(&AO4, LONG_CONSTANT_OT);
+ AO4.value = compile_string(token_text, TRUE, TRUE);
+ }
+ 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_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_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;
+ assembly_operand AO, AO2, AO3, AO4;
+ debug_location spare_debug_location1, spare_debug_location2;
+
+ ASSERT_GLULX();
+
+ if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
+ { /* That is, a full stop, signifying a label */
+
+ get_next_token();
+ if (token_type == SYMBOL_TT)
+ {
+ if (sflags[token_value] & UNKNOWN_SFLAG)
+ { assign_symbol(token_value, next_label, LABEL_T);
+ sflags[token_value] |= USED_SFLAG;
+ assemble_label_no(next_label);
+ define_symbol_label(token_value);
+ next_label++;
+ }
+ else
+ { if (stypes[token_value] != LABEL_T) goto LabelError;
+ if (sflags[token_value] & CHANGE_SFLAG)
+ { sflags[token_value] &= (~(CHANGE_SFLAG));
+ assemble_label_no(svals[token_value]);
+ define_symbol_label(token_value);
+ }
+ else error_named("Duplicate definition of label:", token_text);
+ }
+
+ get_next_token();
+ if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
+ { ebf_error("';'", token_text);
+ put_token_back(); return;
+ }
+
+ /* Interesting point of Inform grammar: a statement can only
+ consist solely of a label when it is immediately followed
+ by a "}". */
+
+ get_next_token();
+ if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
+ { put_token_back(); return;
+ }
+ /* The following line prevents labels from influencing the positions
+ of sequence points. */
+ statement_debug_location = get_token_location();
+ parse_statement(break_label, continue_label);
+ return;
+ }
+ LabelError: ebf_error("label name", token_text);
+ }
+
+ if ((token_type == SEP_TT) && (token_value == HASH_SEP))
+ { parse_directive(TRUE);
+ parse_statement(break_label, continue_label); return;
+ }
+
+ 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++,parse_expression(CONSTANT_CONTEXT));
+ } while (TRUE);
+ finish_array(ln);
+ 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_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_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 */
+
+ 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_label_no(ln2);
+ return;
+ }
+ AO.type = OMITTED_OT;
+ goto ParseUpdate;
+ }
+ put_token_back();
+ if (!match_colon()) break;
+ }
+
+ get_next_token();
+ AO.type = OMITTED_OT;
+ if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
+ { put_token_back();
+ spare_debug_location1 = get_token_location();
+ AO = parse_expression(CONDITION_CONTEXT);
+ if (!match_colon()) break;
+ }
+ get_next_token();
+
+ ParseUpdate:
+ AO2.type = OMITTED_OT; flag = 0;
+ if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
+ { put_token_back();
+ spare_debug_location2 = get_token_location();
+ 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_label_no(ln3);
+ return;
+
+ /* -------------------------------------------------------------------- */
+ /* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
+ /* -------------------------------------------------------------------- */
+
+ case GIVE_CODE:
+ AO = code_generate(parse_expression(QUANTITY_CONTEXT),
+ QUANTITY_CONTEXT, -1);
+ 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
+ { if ((token_type == SYMBOL_TT)
+ && (stypes[token_value] != ATTRIBUTE_T))
+ warning_named("This is not a declared Attribute:",
+ token_text);
+ ln = 1;
+ put_token_back();
+ }
+ AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
+ QUANTITY_CONTEXT, -1);
+ 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;
+ ln2 = 0;
+
+ 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++;
+ }
+
+ code_generate(AO, CONDITION_CONTEXT, ln);
+
+ 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();
+ 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();
+
+ if (ln >= 0) assemble_label_no(ln);
+
+ if (flag)
+ { parse_code_block(break_label, continue_label, 0);
+ if (ln >= 0) assemble_label_no(ln2);
+ }
+
+ 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);
+ 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) &&
+ (stypes[token_value] == GLOBAL_VARIABLE_T)) {
+ INITAOTV(&AO, GLOBALVAR_OT, svals[token_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 = svals[ln];
+ 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);
+ 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);
+ get_next_token();
+ if (token_type == DQ_TT)
+ { INITAOT(&AO4, CONSTANT_OT);
+ AO4.value = compile_string(token_text, TRUE, TRUE);
+ 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_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_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)
+{
+ if (!glulx_mode)
+ parse_statement_z(break_label, continue_label);
+ else
+ parse_statement_g(break_label, continue_label);
+}
+
+/* ========================================================================= */
+/* 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)
+{
+}
+
+/* ========================================================================= */