X-Git-Url: https://jxself.org/git/?p=inform.git;a=blobdiff_plain;f=src%2Fstates.c;fp=src%2Fstates.c;h=cd3b695a5777486926eec58c57e950c634bc8dc5;hp=0000000000000000000000000000000000000000;hb=81ffe9a7de1db0b3a318a053b38882d1b7ab304c;hpb=d1090135a32de7b38b48c55d4e21f95da4c405bc diff --git a/src/states.c b/src/states.c new file mode 100644 index 0000000..cd3b695 --- /dev/null +++ b/src/states.c @@ -0,0 +1,2674 @@ +/* ------------------------------------------------------------------------- */ +/* "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 + or <>. 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. + is equivalent to .) + + To complicate life, the ACTION argument may be a bare action + name or a parenthesized expression. (So 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 "". 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(" 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 -------------------------------------------------- */ + /* print_ret ---------------------------------------------- */ + /* --------------------------------------------------- */ + /* */ + /* is a comma-separated list of items: */ + /* */ + /* */ + /* */ + /* (char) */ + /* (address) */ + /* (string) */ + /* (a) */ + /* (the) */ + /* (The) */ + /* (name) */ + /* (number) */ + /* (property) */ + /* () */ + /* (object) (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 -------------------------------------------------- */ + /* print_ret ---------------------------------------------- */ + /* --------------------------------------------------- */ + /* */ + /* is a comma-separated list of items: */ + /* */ + /* */ + /* */ + /* (char) */ + /* (address) */ + /* (string) */ + /* (a) */ + /* (A) */ + /* (the) */ + /* (The) */ + /* (name) */ + /* (number) */ + /* (property) */ + /* () */ + /* (object) (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 ... -------------------------------------- */ + /* -------------------------------------------------------------------- */ + + 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 until () --------------------------------- */ + /* -------------------------------------------------------------------- */ + + 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 ( : : ) --------- */ + /* -------------------------------------------------------------------- */ + + /* 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=MAX_LOCAL_VARIABLES) + && (flag [~]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 () [else ] -------------------- */ + /* -------------------------------------------------------------------- */ + + 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