/* ------------------------------------------------------------------------- */ /* "states" : Statement translator */ /* */ /* Part of Inform 6.33 */ /* copyright (c) Graham Nelson 1993 - 2014 */ /* */ /* ------------------------------------------------------------------------- */ #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) { AO.marker = STRING_MV; AO.type = LONG_CONSTANT_OT; 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) { AO.type = LONG_CONSTANT_OT; AO.value = token_value; AO.marker = SYMBOL_MV; } else { AO.type = 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. */ { AO.marker = STRING_MV; AO.type = CONSTANT_OT; 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)) { AO.type = BYTECONSTANT_OT; AO.value = 0x0A; AO.marker = 0; assembleg_1(streamchar_gc, AO); AO.type = BYTECONSTANT_OT; AO.value = 1; AO.marker = 0; 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); } AO2.type = HALFCONSTANT_OT; AO2.value = 0x100; AO2.marker = 0; 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); AO2.type = BYTECONSTANT_OT; AO2.value = GOBJFIELD_NAME(); AO2.marker = 0; 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) { AO.type = CONSTANT_OT; AO.value = token_value; AO.marker = SYMBOL_MV; } else { AO.type = 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(); AO2.type = ZEROCONSTANT_OT; AO2.value = 0; AO2.marker = 0; 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) { AO.type = BYTECONSTANT_OT; AO.value = 0x0A; AO.marker = 0; assembleg_1(streamchar_gc, AO); AO.type = BYTECONSTANT_OT; AO.value = 1; AO.marker = 0; 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"); AO3.type = 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; AO2.type = SHORT_CONSTANT_OT; AO2.value = ln2; AO2.marker = 0; AO4.type = VARIABLE_OT; AO4.value = 255; AO4.marker = 0; 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). */ AO.type = SHORT_CONSTANT_OT; AO.marker = 0; 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. */ AO.type = SHORT_CONSTANT_OT; AO.value = 0; AO.marker = 0; AO2.type = SHORT_CONSTANT_OT; AO2.value = 8; AO2.marker = 0; AO3.type = VARIABLE_OT; AO3.value = 255; AO3.marker = 0; assemblez_2_to(loadw_zc, AO, AO2, AO3); if (token_value == ON_MK) { AO4.type = LONG_CONSTANT_OT; AO4.value = 0xfffd; AO4.marker = 0; assemblez_2_to(and_zc, AO4, AO3, AO3); } else { AO4.type = SHORT_CONSTANT_OT; AO4.value = 2; AO4.marker = 0; 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) { AO3.type = SHORT_CONSTANT_OT; AO3.value = 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)) { AO.value = 252; AO.marker = 0; AO.type = SHORT_CONSTANT_OT; 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; 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: AO.marker = 0; AO.type = SHORT_CONSTANT_OT; AO.value = 0; AO2.marker = 0; AO2.type = 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