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