/* ------------------------------------------------------------------------- */
/* "states" : Statement translator */
/* */
-/* Part of Inform 6.35 */
-/* copyright (c) Graham Nelson 1993 - 2021 */
+/* Part of Inform 6.40 */
+/* copyright (c) Graham Nelson 1993 - 2022 */
/* */
/* Inform is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* 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/ */
/* */
/* ------------------------------------------------------------------------- */
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);
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)
+ AO.symindex = token_value;
+ if (symbols[token_value].type != ROUTINE_T)
ebf_error("printing routine name", token_text);
}
- sflags[token_value] |= USED_SFLAG;
+ symbols[token_value].flags |= USED_SFLAG;
PrintByRoutine:
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)
+ AO.symindex = token_value;
+ if (symbols[token_value].type != ROUTINE_T)
ebf_error("printing routine name", token_text);
}
- sflags[token_value] |= USED_SFLAG;
+ symbols[token_value].flags |= USED_SFLAG;
PrintByRoutine:
}
}
-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_error("label name", token_text);
+ return TRUE;
+ }
+
+ if (symbols[token_value].flags & UNKNOWN_SFLAG)
+ { assign_symbol(token_value, next_label, LABEL_T);
+ symbols[token_value].flags |= USED_SFLAG;
+ assemble_label_no(next_label);
+ define_symbol_label(token_value);
+ next_label++;
+ }
+ else
+ { if (symbols[token_value].type != LABEL_T) {
+ ebf_error("label name", token_text);
+ return TRUE;
}
- 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_error("';'", token_text);
+ put_token_back(); return FALSE;
+ }
- /* Interesting point of Inform grammar: a statement can only
- consist solely of a label when it is immediately followed
- by a "}". */
+ /* 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;
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();
}
else error("'do' without matching 'until'");
- assemble_label_no(ln3);
+ assemble_forward_label_no(ln3);
break;
/* -------------------------------------------------------------------- */
sequence_point_follows = FALSE;
if (!execution_never_reaches_here)
assemblez_jump(ln);
- assemble_label_no(ln2);
+ assemble_forward_label_no(ln2);
return;
}
goto ParseUpdate;
}
}
- assemble_label_no(ln3);
+ assemble_forward_label_no(ln3);
return;
/* -------------------------------------------------------------------- */
case GIVE_CODE:
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
QUANTITY_CONTEXT, -1);
+ check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
if ((AO.type == VARIABLE_OT) && (AO.value == 0))
{ INITAOTV(&AO, SHORT_CONSTANT_OT, 252);
if (version_number != 6) assemblez_1(pull_zc, AO);
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;
+ { ln = set_attr_zc;
put_token_back();
}
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
QUANTITY_CONTEXT, -1);
+ check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
if (runtime_error_checking_switch)
{ ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
if (version_number >= 5)
/* -------------------------------------------------------------------- */
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);
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();
}
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;
/* -------------------------------------------------------------------- */
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
QUANTITY_CONTEXT, -1);
AO = code_generate(AO, QUANTITY_CONTEXT, -1);
+ check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
+ check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
{ if (version_number >= 5)
assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
AO.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);
panic_mode_error_recovery(); break;
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),
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)
assemblez_store(AO2, AO);
parse_code_block(ln = next_label++, continue_label, 1);
- assemble_label_no(ln);
+ assemble_forward_label_no(ln);
return;
/* -------------------------------------------------------------------- */
parse_code_block(ln2, ln, 0);
sequence_point_follows = FALSE;
assemblez_jump(ln);
- assemble_label_no(ln2);
+ assemble_forward_label_no(ln2);
return;
/* -------------------------------------------------------------------- */
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;
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();
}
else error("'do' without matching 'until'");
- assemble_label_no(ln3);
+ assemble_forward_label_no(ln3);
break;
/* -------------------------------------------------------------------- */
sequence_point_follows = FALSE;
if (!execution_never_reaches_here)
assembleg_jump(ln);
- assemble_label_no(ln2);
+ assemble_forward_label_no(ln2);
return;
}
goto ParseUpdate;
}
}
- assemble_label_no(ln3);
+ assemble_forward_label_no(ln3);
return;
/* -------------------------------------------------------------------- */
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
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)) {
/* -------------------------------------------------------------------- */
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);
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();
}
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;
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);
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);
sequence_point_follows = TRUE;
ln = symbol_index("Class", -1);
INITAOT(&AO2, CONSTANT_OT);
- AO2.value = svals[ln];
+ AO2.value = symbols[ln].value;
AO2.marker = OBJECT_MV;
assembleg_store(AO, AO2);
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);
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();
assembleg_store(temp_var1, AO);
parse_code_block(ln = next_label++, continue_label, 1);
- assemble_label_no(ln);
+ assemble_forward_label_no(ln);
return;
/* -------------------------------------------------------------------- */
parse_code_block(ln2, ln, 0);
sequence_point_follows = FALSE;
assembleg_jump(ln);
- assemble_label_no(ln2);
+ assemble_forward_label_no(ln2);
return;
/* -------------------------------------------------------------------- */
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;
}
/* ========================================================================= */