2019-02-05 02:44:07 +02:00
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* "states" : Statement translator */
|
|
|
|
/* */
|
2022-03-03 02:10:25 +02:00
|
|
|
/* Part of Inform 6.36 */
|
|
|
|
/* copyright (c) Graham Nelson 1993 - 2022 */
|
2019-02-05 02:44:07 +02:00
|
|
|
/* */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
#include "header.h"
|
|
|
|
|
|
|
|
static int match_colon(void)
|
|
|
|
{ get_next_token();
|
|
|
|
if (token_type == SEP_TT)
|
|
|
|
{ if (token_value == SEMICOLON_SEP)
|
|
|
|
warning("Unlike C, Inform uses ':' to divide parts \
|
|
|
|
of a 'for' loop specification: replacing ';' with ':'");
|
|
|
|
else
|
|
|
|
if (token_value != COLON_SEP)
|
|
|
|
{ ebf_error("':'", token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ ebf_error("':'", token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void match_open_bracket(void)
|
|
|
|
{ get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == OPENB_SEP)) return;
|
|
|
|
put_token_back();
|
|
|
|
ebf_error("'('", token_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void match_close_bracket(void)
|
|
|
|
{ get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP)) return;
|
|
|
|
put_token_back();
|
|
|
|
ebf_error("')'", token_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_action(void)
|
|
|
|
{ int level = 1, args = 0, codegen_action;
|
|
|
|
assembly_operand AO, AO2, AO3, AO4, AO5;
|
|
|
|
|
|
|
|
/* An action statement has the form <ACTION NOUN SECOND, ACTOR>
|
|
|
|
or <<ACTION NOUN SECOND, ACTOR>>. It simply compiles into a call
|
|
|
|
to R_Process() with those four arguments. (The latter form,
|
|
|
|
with double brackets, means "return true afterwards".)
|
|
|
|
|
|
|
|
The R_Process() function should be supplied by the library,
|
|
|
|
although a stub is defined in the veneer.
|
|
|
|
|
|
|
|
The NOUN, SECOND, and ACTOR arguments are optional. If not
|
|
|
|
supplied, R_Process() will be called with fewer arguments.
|
|
|
|
(But if you supply ACTOR, it must be preceded by a comma.
|
|
|
|
<ACTION, ACTOR> is equivalent to <ACTION 0 0, ACTOR>.)
|
|
|
|
|
|
|
|
To complicate life, the ACTION argument may be a bare action
|
|
|
|
name or a parenthesized expression. (So <Take> is equivalent
|
|
|
|
to <(##Take)>.) We have to peek at the first token, checking
|
|
|
|
whether it's an open-paren, to distinguish these cases.
|
|
|
|
|
|
|
|
You may ask why the ACTOR argument is last; the "natural"
|
|
|
|
Inform ordering would be "<floyd, take ball>". True! Sadly,
|
|
|
|
Inform's lexer isn't smart enough to parse this consistently,
|
|
|
|
so we can't do it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
dont_enter_into_symbol_table = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == LESS_SEP))
|
|
|
|
{ level = 2; get_next_token();
|
|
|
|
}
|
|
|
|
dont_enter_into_symbol_table = FALSE;
|
|
|
|
|
|
|
|
/* Peek at the next token; see if it's an open-paren. */
|
|
|
|
if ((token_type==SEP_TT) && (token_value==OPENB_SEP))
|
|
|
|
{ put_token_back();
|
|
|
|
AO2 = parse_expression(ACTION_Q_CONTEXT);
|
|
|
|
codegen_action = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ codegen_action = FALSE;
|
|
|
|
AO2 = action_of_name(token_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
AO3 = zero_operand;
|
|
|
|
AO4 = zero_operand;
|
|
|
|
AO5 = zero_operand;
|
|
|
|
if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
|
|
|
|
{ put_token_back();
|
|
|
|
args = 1;
|
|
|
|
AO3 = parse_expression(ACTION_Q_CONTEXT);
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
}
|
|
|
|
if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
|
|
|
|
{ put_token_back();
|
|
|
|
args = 2;
|
|
|
|
AO4 = parse_expression(QUANTITY_CONTEXT);
|
|
|
|
get_next_token();
|
|
|
|
}
|
|
|
|
if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
|
|
|
|
{
|
|
|
|
ebf_error("',' or '>'", token_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == COMMA_SEP))
|
|
|
|
{
|
|
|
|
if (!glulx_mode && (version_number < 4))
|
|
|
|
{
|
|
|
|
error("<x, y> syntax is not available in Z-code V3 or earlier");
|
|
|
|
}
|
|
|
|
args = 3;
|
|
|
|
AO5 = parse_expression(QUANTITY_CONTEXT);
|
|
|
|
get_next_token();
|
|
|
|
if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
|
|
|
|
{
|
|
|
|
ebf_error("'>'", token_text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (level == 2)
|
|
|
|
{ get_next_token();
|
|
|
|
if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
|
|
|
|
{ put_token_back();
|
|
|
|
ebf_error("'>>'", token_text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!glulx_mode) {
|
|
|
|
|
|
|
|
AO = veneer_routine(R_Process_VR);
|
|
|
|
|
|
|
|
switch(args)
|
|
|
|
{ case 0:
|
|
|
|
if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
|
|
|
|
if (version_number>=5)
|
|
|
|
assemblez_2(call_2n_zc, AO, AO2);
|
|
|
|
else
|
|
|
|
if (version_number==4)
|
|
|
|
assemblez_2_to(call_vs_zc, AO, AO2, temp_var1);
|
|
|
|
else
|
|
|
|
assemblez_2_to(call_zc, AO, AO2, temp_var1);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
|
|
|
|
if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
|
|
|
|
if (version_number>=5)
|
|
|
|
assemblez_3(call_vn_zc, AO, AO2, AO3);
|
|
|
|
else
|
|
|
|
if (version_number==4)
|
|
|
|
assemblez_3_to(call_vs_zc, AO, AO2, AO3, temp_var1);
|
|
|
|
else
|
|
|
|
assemblez_3_to(call_zc, AO, AO2, AO3, temp_var1);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
|
|
|
|
AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
|
|
|
|
if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
|
|
|
|
if (version_number>=5)
|
|
|
|
assemblez_4(call_vn_zc, AO, AO2, AO3, AO4);
|
|
|
|
else
|
|
|
|
if (version_number==4)
|
|
|
|
assemblez_4_to(call_vs_zc, AO, AO2, AO3, AO4, temp_var1);
|
|
|
|
else
|
2020-06-01 20:29:12 +03:00
|
|
|
assemblez_4_to(call_zc, AO, AO2, AO3, AO4, temp_var1);
|
2019-02-05 02:44:07 +02:00
|
|
|
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) &&
|
2022-03-03 02:10:25 +02:00
|
|
|
(symbols[token_value].type == LABEL_T))
|
|
|
|
{ symbols[token_value].flags |= USED_SFLAG;
|
|
|
|
return(symbols[token_value].value);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
2022-03-03 02:10:25 +02:00
|
|
|
if ((token_type == SYMBOL_TT) && (symbols[token_value].flags & UNKNOWN_SFLAG))
|
2019-02-05 02:44:07 +02:00
|
|
|
{ assign_symbol(token_value, next_label, LABEL_T);
|
|
|
|
define_symbol_label(token_value);
|
|
|
|
next_label++;
|
2022-03-03 02:10:25 +02:00
|
|
|
symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG;
|
|
|
|
return(symbols[token_value].value);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ebf_error("label name", token_text);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_print_z(int finally_return)
|
|
|
|
{ int count = 0; assembly_operand AO;
|
|
|
|
|
|
|
|
/* print <printlist> -------------------------------------------------- */
|
|
|
|
/* print_ret <printlist> ---------------------------------------------- */
|
|
|
|
/* <literal-string> --------------------------------------------------- */
|
|
|
|
/* */
|
|
|
|
/* <printlist> is a comma-separated list of items: */
|
|
|
|
/* */
|
|
|
|
/* <literal-string> */
|
|
|
|
/* <other-expression> */
|
|
|
|
/* (char) <expression> */
|
|
|
|
/* (address) <expression> */
|
|
|
|
/* (string) <expression> */
|
|
|
|
/* (a) <expression> */
|
|
|
|
/* (the) <expression> */
|
|
|
|
/* (The) <expression> */
|
|
|
|
/* (name) <expression> */
|
|
|
|
/* (number) <expression> */
|
|
|
|
/* (property) <expression> */
|
|
|
|
/* (<routine>) <expression> */
|
|
|
|
/* (object) <expression> (for use in low-level code only) */
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
do
|
|
|
|
{ AI.text = token_text;
|
|
|
|
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
|
|
|
|
switch(token_type)
|
|
|
|
{ case DQ_TT:
|
|
|
|
if (strlen(token_text) > 32)
|
|
|
|
{ INITAOT(&AO, LONG_CONSTANT_OT);
|
|
|
|
AO.marker = STRING_MV;
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.value = compile_string(token_text, STRCTX_GAME);
|
2019-02-05 02:44:07 +02:00
|
|
|
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:
|
2022-03-03 02:10:25 +02:00
|
|
|
if (symbols[token_value].flags & UNKNOWN_SFLAG)
|
2019-02-05 02:44:07 +02:00
|
|
|
{ INITAOT(&AO, LONG_CONSTANT_OT);
|
|
|
|
AO.value = token_value;
|
|
|
|
AO.marker = SYMBOL_MV;
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.symindex = token_value;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ INITAOT(&AO, LONG_CONSTANT_OT);
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.value = symbols[token_value].value;
|
2019-02-05 02:44:07 +02:00
|
|
|
AO.marker = IROUTINE_MV;
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.symindex = token_value;
|
|
|
|
if (symbols[token_value].type != ROUTINE_T)
|
2019-02-05 02:44:07 +02:00
|
|
|
ebf_error("printing routine name", token_text);
|
|
|
|
}
|
2022-03-03 02:10:25 +02:00
|
|
|
symbols[token_value].flags |= USED_SFLAG;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
PrintByRoutine:
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if (version_number >= 5)
|
|
|
|
assemblez_2(call_2n_zc, AO,
|
|
|
|
code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1));
|
|
|
|
else if (version_number == 4)
|
|
|
|
assemblez_2_to(call_vs_zc, AO,
|
|
|
|
code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1), temp_var1);
|
|
|
|
else
|
|
|
|
assemblez_2_to(call_zc, AO,
|
|
|
|
code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1), temp_var1);
|
|
|
|
goto PrintTermDone;
|
|
|
|
|
|
|
|
default: ebf_error("print specification", token_text);
|
|
|
|
get_next_token();
|
|
|
|
assemblez_1(print_num_zc,
|
|
|
|
code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1));
|
|
|
|
goto PrintTermDone;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
put_token_back(); put_token_back(); put_token_back();
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
assemblez_1(print_num_zc,
|
|
|
|
code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
put_token_back(); misc_keywords.enabled = FALSE;
|
|
|
|
assemblez_1(print_num_zc,
|
|
|
|
code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
PrintTermDone: misc_keywords.enabled = FALSE;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
|
|
|
|
if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
|
|
|
|
{ ebf_error("comma", token_text);
|
|
|
|
panic_mode_error_recovery(); return;
|
|
|
|
}
|
|
|
|
else get_next_token();
|
|
|
|
} while(TRUE);
|
|
|
|
|
|
|
|
if (count == 0) ebf_error("something to print", token_text);
|
|
|
|
if (finally_return)
|
|
|
|
{ assemblez_0(new_line_zc);
|
|
|
|
assemblez_0(rtrue_zc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_print_g(int finally_return)
|
|
|
|
{ int count = 0; assembly_operand AO, AO2;
|
|
|
|
|
|
|
|
/* print <printlist> -------------------------------------------------- */
|
|
|
|
/* print_ret <printlist> ---------------------------------------------- */
|
|
|
|
/* <literal-string> --------------------------------------------------- */
|
|
|
|
/* */
|
|
|
|
/* <printlist> is a comma-separated list of items: */
|
|
|
|
/* */
|
|
|
|
/* <literal-string> */
|
|
|
|
/* <other-expression> */
|
|
|
|
/* (char) <expression> */
|
|
|
|
/* (address) <expression> */
|
|
|
|
/* (string) <expression> */
|
|
|
|
/* (a) <expression> */
|
|
|
|
/* (A) <expression> */
|
|
|
|
/* (the) <expression> */
|
|
|
|
/* (The) <expression> */
|
|
|
|
/* (name) <expression> */
|
|
|
|
/* (number) <expression> */
|
|
|
|
/* (property) <expression> */
|
|
|
|
/* (<routine>) <expression> */
|
|
|
|
/* (object) <expression> (for use in low-level code only) */
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
|
|
|
|
switch(token_type)
|
|
|
|
{ case DQ_TT:
|
|
|
|
/* We can't compile a string into the instruction,
|
|
|
|
so this always goes into the string area. */
|
|
|
|
{ INITAOT(&AO, CONSTANT_OT);
|
|
|
|
AO.marker = STRING_MV;
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.value = compile_string(token_text, STRCTX_GAME);
|
2019-02-05 02:44:07 +02:00
|
|
|
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:
|
2022-03-03 02:10:25 +02:00
|
|
|
if (symbols[token_value].flags & UNKNOWN_SFLAG)
|
2019-02-05 02:44:07 +02:00
|
|
|
{ INITAOT(&AO, CONSTANT_OT);
|
|
|
|
AO.value = token_value;
|
|
|
|
AO.marker = SYMBOL_MV;
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.symindex = token_value;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ INITAOT(&AO, CONSTANT_OT);
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.value = symbols[token_value].value;
|
2019-02-05 02:44:07 +02:00
|
|
|
AO.marker = IROUTINE_MV;
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.symindex = token_value;
|
|
|
|
if (symbols[token_value].type != ROUTINE_T)
|
2019-02-05 02:44:07 +02:00
|
|
|
ebf_error("printing routine name", token_text);
|
|
|
|
}
|
2022-03-03 02:10:25 +02:00
|
|
|
symbols[token_value].flags |= USED_SFLAG;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2022-03-03 02:10:25 +02:00
|
|
|
if (symbols[token_value].flags & UNKNOWN_SFLAG)
|
2019-02-05 02:44:07 +02:00
|
|
|
{ assign_symbol(token_value, next_label, LABEL_T);
|
2022-03-03 02:10:25 +02:00
|
|
|
symbols[token_value].flags |= USED_SFLAG;
|
2019-02-05 02:44:07 +02:00
|
|
|
assemble_label_no(next_label);
|
|
|
|
define_symbol_label(token_value);
|
|
|
|
next_label++;
|
|
|
|
}
|
|
|
|
else
|
2022-03-03 02:10:25 +02:00
|
|
|
{ if (symbols[token_value].type != LABEL_T) goto LabelError;
|
|
|
|
if (symbols[token_value].flags & CHANGE_SFLAG)
|
|
|
|
{ symbols[token_value].flags &= (~(CHANGE_SFLAG));
|
|
|
|
assemble_label_no(symbols[token_value].value);
|
2019-02-05 02:44:07 +02:00
|
|
|
define_symbol_label(token_value);
|
|
|
|
}
|
|
|
|
else error_named("Duplicate definition of label:", token_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
|
|
|
|
{ ebf_error("';'", token_text);
|
|
|
|
put_token_back(); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Interesting point of Inform grammar: a statement can only
|
|
|
|
consist solely of a label when it is immediately followed
|
|
|
|
by a "}". */
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
|
|
|
|
{ put_token_back(); return;
|
|
|
|
}
|
|
|
|
statement_debug_location = get_token_location();
|
|
|
|
parse_statement(break_label, continue_label);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LabelError: ebf_error("label name", token_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == HASH_SEP))
|
|
|
|
{ parse_directive(TRUE);
|
|
|
|
parse_statement(break_label, continue_label); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == AT_SEP))
|
|
|
|
{ parse_assembly(); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
|
|
|
|
|
|
|
|
if (token_type == DQ_TT)
|
|
|
|
{ parse_print_z(TRUE); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == LESS_SEP))
|
|
|
|
{ parse_action(); goto StatementTerminator; }
|
|
|
|
|
|
|
|
if (token_type == EOF_TT)
|
|
|
|
{ ebf_error("statement", token_text); return; }
|
|
|
|
|
|
|
|
if (token_type != STATEMENT_TT)
|
|
|
|
{ put_token_back();
|
|
|
|
AO = parse_expression(VOID_CONTEXT);
|
|
|
|
code_generate(AO, VOID_CONTEXT, -1);
|
|
|
|
if (vivc_flag) { panic_mode_error_recovery(); return; }
|
|
|
|
goto StatementTerminator;
|
|
|
|
}
|
|
|
|
|
|
|
|
statements.enabled = FALSE;
|
|
|
|
|
|
|
|
switch(token_value)
|
|
|
|
{
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* box <string-1> ... <string-n> -------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case BOX_CODE:
|
|
|
|
if (version_number == 3)
|
|
|
|
warning("The 'box' statement has no effect in a version 3 game");
|
|
|
|
INITAOT(&AO3, LONG_CONSTANT_OT);
|
|
|
|
AO3.value = begin_table_array();
|
|
|
|
AO3.marker = ARRAY_MV;
|
|
|
|
ln = 0; ln2 = 0;
|
|
|
|
do
|
|
|
|
{ get_next_token();
|
|
|
|
if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
|
|
|
|
break;
|
|
|
|
if (token_type != DQ_TT)
|
|
|
|
ebf_error("text of box line in double-quotes",
|
|
|
|
token_text);
|
|
|
|
{ int i, j;
|
|
|
|
for (i=0, j=0; token_text[i] != 0; j++)
|
|
|
|
if (token_text[i] == '@')
|
|
|
|
{ if (token_text[i+1] == '@')
|
|
|
|
{ i = i + 2;
|
|
|
|
while (isdigit(token_text[i])) i++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ i++;
|
|
|
|
if (token_text[i] != 0) i++;
|
|
|
|
if (token_text[i] != 0) i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else i++;
|
|
|
|
if (j > ln2) ln2 = j;
|
|
|
|
}
|
|
|
|
put_token_back();
|
2020-06-01 20:29:12 +03:00
|
|
|
array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
|
2019-02-05 02:44:07 +02:00
|
|
|
} while (TRUE);
|
2020-06-01 20:29:12 +03:00
|
|
|
finish_array(ln, FALSE);
|
2019-02-05 02:44:07 +02:00
|
|
|
if (ln == 0)
|
|
|
|
error("No lines of text given for 'box' display");
|
|
|
|
|
|
|
|
if (version_number == 3) return;
|
|
|
|
|
|
|
|
INITAOTV(&AO2, SHORT_CONSTANT_OT, ln2);
|
|
|
|
INITAOTV(&AO4, VARIABLE_OT, 255);
|
|
|
|
assemblez_3_to(call_vs_zc, veneer_routine(Box__Routine_VR),
|
|
|
|
AO2, AO3, AO4);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* break -------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case BREAK_CODE:
|
|
|
|
if (break_label == -1)
|
|
|
|
error("'break' can only be used in a loop or 'switch' block");
|
|
|
|
else
|
|
|
|
assemblez_jump(break_label);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* continue ----------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case CONTINUE_CODE:
|
|
|
|
if (continue_label == -1)
|
|
|
|
error("'continue' can only be used in a loop block");
|
|
|
|
else
|
|
|
|
assemblez_jump(continue_label);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* do <codeblock> until (<condition>) --------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case DO_CODE:
|
|
|
|
assemble_label_no(ln = next_label++);
|
|
|
|
ln2 = next_label++; ln3 = next_label++;
|
|
|
|
parse_code_block(ln3, ln2, 0);
|
|
|
|
statements.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == STATEMENT_TT)
|
|
|
|
&& (token_value == UNTIL_CODE))
|
|
|
|
{ assemble_label_no(ln2);
|
|
|
|
match_open_bracket();
|
|
|
|
AO = parse_expression(CONDITION_CONTEXT);
|
|
|
|
match_close_bracket();
|
|
|
|
code_generate(AO, CONDITION_CONTEXT, ln);
|
|
|
|
}
|
|
|
|
else error("'do' without matching 'until'");
|
|
|
|
|
|
|
|
assemble_label_no(ln3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* font on/off -------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case FONT_CODE:
|
|
|
|
misc_keywords.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
if ((token_type != MISC_KEYWORD_TT)
|
|
|
|
|| ((token_value != ON_MK)
|
|
|
|
&& (token_value != OFF_MK)))
|
|
|
|
{ ebf_error("'on' or 'off'", token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version_number >= 5)
|
|
|
|
{ /* Use the V5 @set_font opcode, setting font 4
|
|
|
|
(for font off) or 1 (for font on). */
|
|
|
|
INITAOT(&AO, SHORT_CONSTANT_OT);
|
|
|
|
if (token_value == ON_MK)
|
|
|
|
AO.value = 1;
|
|
|
|
else
|
|
|
|
AO.value = 4;
|
|
|
|
assemblez_1_to(set_font_zc, AO, temp_var1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the fixed-pitch header bit. */
|
|
|
|
INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
|
|
|
|
INITAOTV(&AO2, SHORT_CONSTANT_OT, 8);
|
|
|
|
INITAOTV(&AO3, VARIABLE_OT, 255);
|
|
|
|
assemblez_2_to(loadw_zc, AO, AO2, AO3);
|
|
|
|
|
|
|
|
if (token_value == ON_MK)
|
|
|
|
{ INITAOTV(&AO4, LONG_CONSTANT_OT, 0xfffd);
|
|
|
|
assemblez_2_to(and_zc, AO4, AO3, AO3);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ INITAOTV(&AO4, SHORT_CONSTANT_OT, 2);
|
|
|
|
assemblez_2_to(or_zc, AO4, AO3, AO3);
|
|
|
|
}
|
|
|
|
|
|
|
|
assemblez_3(storew_zc, AO, AO2, AO3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* for (<initialisation> : <continue-condition> : <updating>) --------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/* Note that it's legal for any or all of the three sections of a
|
|
|
|
'for' specification to be empty. This 'for' implementation
|
|
|
|
often wastes 3 bytes with a redundant branch rather than keep
|
|
|
|
expression parse trees for long periods (as previous versions
|
|
|
|
of Inform did, somewhat crudely by simply storing the textual
|
|
|
|
form of a 'for' loop). It is adequate for now. */
|
|
|
|
|
|
|
|
case FOR_CODE:
|
|
|
|
match_open_bracket();
|
|
|
|
get_next_token();
|
|
|
|
|
|
|
|
/* Initialisation code */
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.type = OMITTED_OT;
|
|
|
|
spare_debug_location1 = statement_debug_location;
|
|
|
|
AO2.type = OMITTED_OT; flag = 0;
|
|
|
|
spare_debug_location2 = statement_debug_location;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
goto ParseUpdate;
|
|
|
|
}
|
|
|
|
put_token_back();
|
|
|
|
if (!match_colon()) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
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:
|
|
|
|
if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
|
|
|
|
{ put_token_back();
|
|
|
|
spare_debug_location2 = get_token_location();
|
|
|
|
AO2 = parse_expression(VOID_CONTEXT);
|
|
|
|
match_close_bracket();
|
|
|
|
flag = test_for_incdec(AO2);
|
|
|
|
}
|
|
|
|
|
|
|
|
ln = next_label++;
|
|
|
|
ln2 = next_label++;
|
|
|
|
ln3 = next_label++;
|
|
|
|
|
|
|
|
if ((AO2.type == OMITTED_OT) || (flag != 0))
|
|
|
|
{
|
|
|
|
assemble_label_no(ln);
|
|
|
|
if (flag==0) assemble_label_no(ln2);
|
|
|
|
|
|
|
|
/* The "finished yet?" condition */
|
|
|
|
|
|
|
|
if (AO.type != OMITTED_OT)
|
|
|
|
{ sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = spare_debug_location1;
|
|
|
|
code_generate(AO, CONDITION_CONTEXT, ln3);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* This is the jump which could be avoided with the aid
|
|
|
|
of long-term expression storage */
|
|
|
|
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
assemblez_jump(ln2);
|
|
|
|
|
|
|
|
/* The "update" part */
|
|
|
|
|
|
|
|
assemble_label_no(ln);
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = spare_debug_location2;
|
|
|
|
code_generate(AO2, VOID_CONTEXT, -1);
|
|
|
|
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
|
|
|
|
/* The "finished yet?" condition */
|
|
|
|
|
|
|
|
if (AO.type != OMITTED_OT)
|
|
|
|
{ sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = spare_debug_location1;
|
|
|
|
code_generate(AO, CONDITION_CONTEXT, ln3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flag != 0)
|
|
|
|
{
|
|
|
|
/* In this optimised case, update code is at the end
|
|
|
|
of the loop block, so "continue" goes there */
|
|
|
|
|
|
|
|
parse_code_block(ln3, ln2, 0);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = spare_debug_location2;
|
|
|
|
if (flag > 0)
|
|
|
|
{ INITAOTV(&AO3, SHORT_CONSTANT_OT, flag);
|
|
|
|
if (module_switch
|
|
|
|
&& (flag>=MAX_LOCAL_VARIABLES) && (flag<LOWEST_SYSTEM_VAR_NUMBER))
|
|
|
|
AO3.marker = VARIABLE_MV;
|
|
|
|
assemblez_1(inc_zc, AO3);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ INITAOTV(&AO3, SHORT_CONSTANT_OT, -flag);
|
|
|
|
if ((module_switch) && (flag>=MAX_LOCAL_VARIABLES)
|
|
|
|
&& (flag<LOWEST_SYSTEM_VAR_NUMBER))
|
|
|
|
AO3.marker = VARIABLE_MV;
|
|
|
|
assemblez_1(dec_zc, AO3);
|
|
|
|
}
|
|
|
|
assemblez_jump(ln);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* In the unoptimised case, update code is at the
|
|
|
|
start of the loop block, so "continue" goes there */
|
|
|
|
|
|
|
|
parse_code_block(ln3, ln, 0);
|
|
|
|
if (!execution_never_reaches_here)
|
|
|
|
{ sequence_point_follows = FALSE;
|
|
|
|
assemblez_jump(ln);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assemble_label_no(ln3);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case GIVE_CODE:
|
|
|
|
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
|
2019-02-05 02:44:07 +02:00
|
|
|
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
|
2022-03-03 02:10:25 +02:00
|
|
|
{ ln = set_attr_zc;
|
2019-02-05 02:44:07 +02:00
|
|
|
put_token_back();
|
|
|
|
}
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
|
2019-02-05 02:44:07 +02:00
|
|
|
if (runtime_error_checking_switch)
|
|
|
|
{ ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
|
|
|
|
if (version_number >= 5)
|
|
|
|
assemblez_3(call_vn_zc, veneer_routine(ln2),
|
|
|
|
AO, AO2);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assemblez_3_to(call_zc, veneer_routine(ln2),
|
|
|
|
AO, AO2, temp_var1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
assemblez_2(ln, AO, AO2);
|
|
|
|
} while(TRUE);
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* if (<condition>) <codeblock> [else <codeblock>] -------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case IF_CODE:
|
|
|
|
flag = FALSE;
|
|
|
|
ln2 = 0;
|
|
|
|
|
|
|
|
match_open_bracket();
|
|
|
|
AO = parse_expression(CONDITION_CONTEXT);
|
|
|
|
match_close_bracket();
|
|
|
|
|
|
|
|
statements.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
|
|
|
|
ln = -4;
|
|
|
|
else
|
|
|
|
if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
|
|
|
|
ln = -3;
|
|
|
|
else
|
|
|
|
{ put_token_back();
|
|
|
|
ln = next_label++;
|
|
|
|
}
|
|
|
|
|
|
|
|
code_generate(AO, CONDITION_CONTEXT, ln);
|
|
|
|
|
|
|
|
if (ln >= 0) parse_code_block(break_label, continue_label, 0);
|
|
|
|
else
|
|
|
|
{ get_next_token();
|
|
|
|
if ((token_type != SEP_TT)
|
|
|
|
|| (token_value != SEMICOLON_SEP))
|
|
|
|
{ ebf_error("';'", token_text);
|
|
|
|
put_token_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
statements.enabled = TRUE;
|
|
|
|
get_next_token();
|
2022-03-03 02:10:25 +02:00
|
|
|
|
|
|
|
/* An #if directive around the ELSE clause is legal. */
|
|
|
|
while ((token_type == SEP_TT) && (token_value == HASH_SEP))
|
|
|
|
{ parse_directive(TRUE);
|
|
|
|
statements.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
}
|
|
|
|
|
2019-02-05 02:44:07 +02:00
|
|
|
if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
|
|
|
|
{ flag = TRUE;
|
|
|
|
if (ln >= 0)
|
|
|
|
{ ln2 = next_label++;
|
|
|
|
if (!execution_never_reaches_here)
|
|
|
|
{ sequence_point_follows = FALSE;
|
|
|
|
assemblez_jump(ln2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else put_token_back();
|
|
|
|
|
|
|
|
if (ln >= 0) assemble_label_no(ln);
|
|
|
|
|
|
|
|
if (flag)
|
|
|
|
{ parse_code_block(break_label, continue_label, 0);
|
|
|
|
if (ln >= 0) assemble_label_no(ln2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* inversion ---------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case INVERSION_CODE:
|
|
|
|
INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
|
|
|
|
INITAOT(&AO2, SHORT_CONSTANT_OT);
|
|
|
|
|
|
|
|
AO2.value = 60;
|
|
|
|
assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
|
|
|
|
assemblez_1(print_char_zc, temp_var1);
|
|
|
|
AO2.value = 61;
|
|
|
|
assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
|
|
|
|
assemblez_1(print_char_zc, temp_var1);
|
|
|
|
AO2.value = 62;
|
|
|
|
assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
|
|
|
|
assemblez_1(print_char_zc, temp_var1);
|
|
|
|
AO2.value = 63;
|
|
|
|
assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
|
|
|
|
assemblez_1(print_char_zc, temp_var1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* jump <label> ------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case JUMP_CODE:
|
|
|
|
assemblez_jump(parse_label());
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* move <expression> to <expression> ---------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case MOVE_CODE:
|
|
|
|
misc_keywords.enabled = TRUE;
|
|
|
|
AO = parse_expression(QUANTITY_CONTEXT);
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
if ((token_type != MISC_KEYWORD_TT)
|
|
|
|
|| (token_value != TO_MK))
|
|
|
|
{ ebf_error("'to'", token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
AO = code_generate(AO, QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
|
|
|
|
check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
|
2019-02-05 02:44:07 +02:00
|
|
|
if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
|
|
|
|
{ if (version_number >= 5)
|
|
|
|
assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
|
|
|
|
AO, AO2);
|
|
|
|
else
|
|
|
|
{ assemblez_3_to(call_zc, veneer_routine(RT__ChT_VR),
|
|
|
|
AO, AO2, temp_var1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
assemblez_2(insert_obj_zc, AO, AO2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* new_line ----------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case NEW_LINE_CODE: assemblez_0(new_line_zc); break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* objectloop (<initialisation>) <codeblock> -------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case OBJECTLOOP_CODE:
|
|
|
|
|
|
|
|
match_open_bracket();
|
|
|
|
get_next_token();
|
|
|
|
INITAOT(&AO, VARIABLE_OT);
|
|
|
|
if (token_type == LOCAL_VARIABLE_TT)
|
|
|
|
AO.value = token_value;
|
|
|
|
else
|
|
|
|
if ((token_type == SYMBOL_TT) &&
|
2022-03-03 02:10:25 +02:00
|
|
|
(symbols[token_value].type == GLOBAL_VARIABLE_T))
|
|
|
|
AO.value = symbols[token_value].value;
|
2019-02-05 02:44:07 +02:00
|
|
|
else
|
|
|
|
{ ebf_error("'objectloop' variable", token_text);
|
|
|
|
panic_mode_error_recovery(); break;
|
|
|
|
}
|
|
|
|
if ((module_switch) && (AO.value >= MAX_LOCAL_VARIABLES)
|
|
|
|
&& (AO.value < LOWEST_SYSTEM_VAR_NUMBER))
|
|
|
|
AO.marker = VARIABLE_MV;
|
|
|
|
misc_keywords.enabled = TRUE;
|
|
|
|
get_next_token(); flag = TRUE;
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
|
|
|
|
flag = FALSE;
|
|
|
|
|
|
|
|
ln = 0;
|
|
|
|
if ((token_type == MISC_KEYWORD_TT)
|
|
|
|
&& (token_value == NEAR_MK)) ln = 1;
|
|
|
|
if ((token_type == MISC_KEYWORD_TT)
|
|
|
|
&& (token_value == FROM_MK)) ln = 2;
|
|
|
|
if ((token_type == CND_TT) && (token_value == IN_COND))
|
|
|
|
{ get_next_token();
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
|
|
|
|
ln = 3;
|
|
|
|
put_token_back();
|
|
|
|
put_token_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ln > 0)
|
|
|
|
{ /* Old style (Inform 5) objectloops: note that we
|
|
|
|
implement objectloop (a in b) in the old way since
|
|
|
|
this runs through objects in a different order from
|
|
|
|
the new way, and there may be existing Inform code
|
|
|
|
relying on this. */
|
|
|
|
assembly_operand AO4;
|
|
|
|
INITAO(&AO4);
|
|
|
|
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
match_close_bracket();
|
|
|
|
if (ln == 1)
|
|
|
|
{ INITAOTV(&AO3, VARIABLE_OT, 0);
|
|
|
|
if (runtime_error_checking_switch)
|
|
|
|
AO2 = check_nonzero_at_runtime(AO2, -1,
|
|
|
|
OBJECTLOOP_RTE);
|
|
|
|
assemblez_1_to(get_parent_zc, AO2, AO3);
|
|
|
|
assemblez_objcode(get_child_zc, AO3, AO3, -2, TRUE);
|
|
|
|
AO2 = AO3;
|
|
|
|
}
|
|
|
|
if (ln == 3)
|
|
|
|
{ INITAOTV(&AO3, VARIABLE_OT, 0);
|
|
|
|
if (runtime_error_checking_switch)
|
|
|
|
{ AO4 = AO2;
|
|
|
|
AO2 = check_nonzero_at_runtime(AO2, -1,
|
|
|
|
CHILD_RTE);
|
|
|
|
}
|
|
|
|
assemblez_objcode(get_child_zc, AO2, AO3, -2, TRUE);
|
|
|
|
AO2 = AO3;
|
|
|
|
}
|
|
|
|
assemblez_store(AO, AO2);
|
|
|
|
assemblez_1_branch(jz_zc, AO, ln2 = next_label++, TRUE);
|
|
|
|
assemble_label_no(ln4 = next_label++);
|
|
|
|
parse_code_block(ln2, ln3 = next_label++, 0);
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
assemble_label_no(ln3);
|
|
|
|
if (runtime_error_checking_switch)
|
|
|
|
{ AO2 = check_nonzero_at_runtime(AO, ln2,
|
|
|
|
OBJECTLOOP2_RTE);
|
|
|
|
if ((ln == 3)
|
|
|
|
&& ((AO4.type != VARIABLE_OT)||(AO4.value != 0))
|
|
|
|
&& ((AO4.type != VARIABLE_OT)
|
|
|
|
||(AO4.value != AO.value)))
|
|
|
|
{ assembly_operand en_ao;
|
|
|
|
INITAOTV(&en_ao, SHORT_CONSTANT_OT, OBJECTLOOP_BROKEN_RTE);
|
|
|
|
assemblez_2_branch(jin_zc, AO, AO4,
|
|
|
|
next_label, TRUE);
|
|
|
|
assemblez_3(call_vn_zc, veneer_routine(RT__Err_VR),
|
|
|
|
en_ao, AO);
|
|
|
|
assemblez_jump(ln2);
|
|
|
|
assemble_label_no(next_label++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else AO2 = AO;
|
|
|
|
assemblez_objcode(get_sibling_zc, AO2, AO, ln4, TRUE);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
INITAOTV(&AO2, SHORT_CONSTANT_OT, 1);
|
|
|
|
assemblez_store(AO, AO2);
|
|
|
|
|
|
|
|
assemble_label_no(ln = next_label++);
|
|
|
|
ln2 = next_label++;
|
|
|
|
ln3 = next_label++;
|
|
|
|
if (flag)
|
|
|
|
{ put_token_back();
|
|
|
|
put_token_back();
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
code_generate(parse_expression(CONDITION_CONTEXT),
|
|
|
|
CONDITION_CONTEXT, ln3);
|
|
|
|
match_close_bracket();
|
|
|
|
}
|
|
|
|
parse_code_block(ln2, ln3, 0);
|
|
|
|
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
assemble_label_no(ln3);
|
|
|
|
assemblez_inc(AO);
|
|
|
|
INITAOTV(&AO2, LONG_CONSTANT_OT, no_objects);
|
|
|
|
AO2.marker = NO_OBJS_MV;
|
|
|
|
assemblez_2_branch(jg_zc, AO, AO2, ln2, TRUE);
|
|
|
|
assemblez_jump(ln);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* (see routine above) ------------------------------------------------ */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case PRINT_CODE:
|
|
|
|
get_next_token();
|
|
|
|
parse_print_z(FALSE); return;
|
|
|
|
case PRINT_RET_CODE:
|
|
|
|
get_next_token();
|
|
|
|
parse_print_z(TRUE); return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* quit --------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case QUIT_CODE: assemblez_0(quit_zc); break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* read <expression> <expression> [<Routine>] ------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case READ_CODE:
|
|
|
|
INITAOTV(&AO, VARIABLE_OT, 252);
|
|
|
|
assemblez_store(AO,
|
|
|
|
code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1));
|
|
|
|
if (version_number > 3)
|
|
|
|
{ INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
|
|
|
|
INITAOTV(&AO4, SHORT_CONSTANT_OT, 0);
|
|
|
|
assemblez_3(storeb_zc, AO, AO3, AO4);
|
|
|
|
}
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
|
|
|
|
put_token_back();
|
|
|
|
else
|
|
|
|
{ if (version_number == 3)
|
|
|
|
error(
|
|
|
|
"In Version 3 no status-line drawing routine can be given");
|
|
|
|
else
|
|
|
|
{ assembly_operand AO5;
|
|
|
|
/* Move the temp4 (buffer) value to the stack,
|
|
|
|
since the routine might alter temp4. */
|
|
|
|
assemblez_store(stack_pointer, AO);
|
|
|
|
AO = stack_pointer;
|
|
|
|
put_token_back();
|
|
|
|
AO5 = parse_expression(CONSTANT_CONTEXT);
|
|
|
|
|
|
|
|
if (version_number >= 5)
|
|
|
|
assemblez_1(call_1n_zc, AO5);
|
|
|
|
else
|
|
|
|
assemblez_1_to(call_zc, AO5, temp_var1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version_number > 4)
|
|
|
|
{ assemblez_2_to(aread_zc, AO, AO2, temp_var1);
|
|
|
|
}
|
|
|
|
else assemblez_2(sread_zc, AO, AO2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* remove <expression> ------------------------------------------------ */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case REMOVE_CODE:
|
|
|
|
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
|
2019-02-05 02:44:07 +02:00
|
|
|
if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
|
|
|
|
{ if (version_number >= 5)
|
|
|
|
assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR),
|
|
|
|
AO);
|
|
|
|
else
|
|
|
|
{ assemblez_2_to(call_zc, veneer_routine(RT__ChR_VR),
|
|
|
|
AO, temp_var1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
assemblez_1(remove_obj_zc, AO);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* restore <label> ---------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case RESTORE_CODE:
|
|
|
|
if (version_number < 5)
|
|
|
|
assemblez_0_branch(restore_zc, parse_label(), TRUE);
|
|
|
|
else
|
|
|
|
{ INITAOTV(&AO2, SHORT_CONSTANT_OT, 2);
|
|
|
|
assemblez_0_to(restore_zc, temp_var1);
|
|
|
|
assemblez_2_branch(je_zc, temp_var1, AO2, parse_label(), TRUE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* return [<expression>] ---------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case RETURN_CODE:
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
|
|
|
|
{ assemblez_0(rtrue_zc); return; }
|
|
|
|
put_token_back();
|
|
|
|
AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 0)
|
|
|
|
&& (AO.marker == 0))
|
|
|
|
{ assemblez_0(rfalse_zc); break; }
|
|
|
|
if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 1)
|
|
|
|
&& (AO.marker == 0))
|
|
|
|
{ assemblez_0(rtrue_zc); break; }
|
|
|
|
if ((AO.type == VARIABLE_OT) && (AO.value == 0))
|
|
|
|
{ assemblez_0(ret_popped_zc); break; }
|
|
|
|
assemblez_1(ret_zc, AO);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* rfalse ------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case RFALSE_CODE: assemblez_0(rfalse_zc); break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* rtrue -------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case RTRUE_CODE: assemblez_0(rtrue_zc); break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* save <label> ------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case SAVE_CODE:
|
|
|
|
if (version_number < 5)
|
|
|
|
assemblez_0_branch(save_zc, parse_label(), TRUE);
|
|
|
|
else
|
|
|
|
{ INITAOTV(&AO, VARIABLE_OT, 255);
|
|
|
|
assemblez_0_to(save_zc, AO);
|
|
|
|
assemblez_1_branch(jz_zc, AO, parse_label(), FALSE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* spaces <expression> ------------------------------------------------ */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case SPACES_CODE:
|
|
|
|
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
INITAOTV(&AO2, VARIABLE_OT, 255);
|
|
|
|
|
|
|
|
assemblez_store(AO2, AO);
|
|
|
|
|
|
|
|
INITAOTV(&AO, SHORT_CONSTANT_OT, 32);
|
|
|
|
INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
|
|
|
|
|
|
|
|
assemblez_2_branch(jl_zc, AO2, AO3, ln = next_label++, TRUE);
|
|
|
|
assemble_label_no(ln2 = next_label++);
|
|
|
|
assemblez_1(print_char_zc, AO);
|
|
|
|
assemblez_dec(AO2);
|
|
|
|
assemblez_1_branch(jz_zc, AO2, ln2, FALSE);
|
|
|
|
assemble_label_no(ln);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* string <expression> <literal-string> ------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case STRING_CODE:
|
|
|
|
INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
|
|
|
|
INITAOTV(&AO2, SHORT_CONSTANT_OT, 12);
|
|
|
|
INITAOTV(&AO3, VARIABLE_OT, 252);
|
|
|
|
assemblez_2_to(loadw_zc, AO, AO2, AO3);
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
if (is_constant_ot(AO2.type) && AO2.marker == 0) {
|
|
|
|
/* Compile-time check */
|
|
|
|
if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
|
|
|
|
error_max_dynamic_strings(AO2.value);
|
|
|
|
AO2.value = 0;
|
|
|
|
}
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
get_next_token();
|
|
|
|
if (token_type == DQ_TT)
|
|
|
|
{ INITAOT(&AO4, LONG_CONSTANT_OT);
|
2022-03-03 02:10:25 +02:00
|
|
|
/* 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);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ put_token_back();
|
|
|
|
AO4 = parse_expression(CONSTANT_CONTEXT);
|
|
|
|
}
|
|
|
|
assemblez_3(storew_zc, AO3, AO2, AO4);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* style roman/reverse/bold/underline/fixed --------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case STYLE_CODE:
|
|
|
|
if (version_number==3)
|
|
|
|
{ error(
|
|
|
|
"The 'style' statement cannot be used for Version 3 games");
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
misc_keywords.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
if ((token_type != MISC_KEYWORD_TT)
|
|
|
|
|| ((token_value != ROMAN_MK)
|
|
|
|
&& (token_value != REVERSE_MK)
|
|
|
|
&& (token_value != BOLD_MK)
|
|
|
|
&& (token_value != UNDERLINE_MK)
|
|
|
|
&& (token_value != FIXED_MK)))
|
|
|
|
{ ebf_error(
|
|
|
|
"'roman', 'bold', 'underline', 'reverse' or 'fixed'",
|
|
|
|
token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
INITAOT(&AO, SHORT_CONSTANT_OT);
|
|
|
|
switch(token_value)
|
|
|
|
{ case ROMAN_MK: AO.value = 0; break;
|
|
|
|
case REVERSE_MK: AO.value = 1; break;
|
|
|
|
case BOLD_MK: AO.value = 2; break;
|
|
|
|
case UNDERLINE_MK: AO.value = 4; break;
|
|
|
|
case FIXED_MK: AO.value = 8; break;
|
|
|
|
}
|
|
|
|
assemblez_1(set_text_style_zc, AO); break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* switch (<expression>) <codeblock> ---------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case SWITCH_CODE:
|
|
|
|
match_open_bracket();
|
|
|
|
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
match_close_bracket();
|
|
|
|
|
|
|
|
INITAOTV(&AO2, VARIABLE_OT, 255);
|
|
|
|
assemblez_store(AO2, AO);
|
|
|
|
|
|
|
|
parse_code_block(ln = next_label++, continue_label, 1);
|
|
|
|
assemble_label_no(ln);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* while (<condition>) <codeblock> ------------------------------------ */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case WHILE_CODE:
|
|
|
|
assemble_label_no(ln = next_label++);
|
|
|
|
match_open_bracket();
|
|
|
|
|
|
|
|
code_generate(parse_expression(CONDITION_CONTEXT),
|
|
|
|
CONDITION_CONTEXT, ln2 = next_label++);
|
|
|
|
match_close_bracket();
|
|
|
|
|
|
|
|
parse_code_block(ln2, ln, 0);
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
assemblez_jump(ln);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case SDEFAULT_CODE:
|
|
|
|
error("'default' without matching 'switch'"); break;
|
|
|
|
case ELSE_CODE:
|
|
|
|
error("'else' without matching 'if'"); break;
|
|
|
|
case UNTIL_CODE:
|
|
|
|
error("'until' without matching 'do'");
|
|
|
|
panic_mode_error_recovery(); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
StatementTerminator:
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
|
|
|
|
{ ebf_error("';'", token_text);
|
|
|
|
put_token_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_statement_g(int break_label, int continue_label)
|
|
|
|
{ int ln, ln2, ln3, ln4, flag, onstack;
|
|
|
|
assembly_operand AO, AO2, AO3, AO4;
|
|
|
|
debug_location spare_debug_location1, spare_debug_location2;
|
|
|
|
|
|
|
|
ASSERT_GLULX();
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
|
|
|
|
{ /* That is, a full stop, signifying a label */
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if (token_type == SYMBOL_TT)
|
|
|
|
{
|
2022-03-03 02:10:25 +02:00
|
|
|
if (symbols[token_value].flags & UNKNOWN_SFLAG)
|
2019-02-05 02:44:07 +02:00
|
|
|
{ assign_symbol(token_value, next_label, LABEL_T);
|
2022-03-03 02:10:25 +02:00
|
|
|
symbols[token_value].flags |= USED_SFLAG;
|
2019-02-05 02:44:07 +02:00
|
|
|
assemble_label_no(next_label);
|
|
|
|
define_symbol_label(token_value);
|
|
|
|
next_label++;
|
|
|
|
}
|
|
|
|
else
|
2022-03-03 02:10:25 +02:00
|
|
|
{ if (symbols[token_value].type != LABEL_T) goto LabelError;
|
|
|
|
if (symbols[token_value].flags & CHANGE_SFLAG)
|
|
|
|
{ symbols[token_value].flags &= (~(CHANGE_SFLAG));
|
|
|
|
assemble_label_no(symbols[token_value].value);
|
2019-02-05 02:44:07 +02:00
|
|
|
define_symbol_label(token_value);
|
|
|
|
}
|
|
|
|
else error_named("Duplicate definition of label:", token_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
|
|
|
|
{ ebf_error("';'", token_text);
|
|
|
|
put_token_back(); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Interesting point of Inform grammar: a statement can only
|
|
|
|
consist solely of a label when it is immediately followed
|
|
|
|
by a "}". */
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
|
|
|
|
{ put_token_back(); return;
|
|
|
|
}
|
|
|
|
/* The following line prevents labels from influencing the positions
|
|
|
|
of sequence points. */
|
|
|
|
statement_debug_location = get_token_location();
|
|
|
|
parse_statement(break_label, continue_label);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LabelError: ebf_error("label name", token_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == HASH_SEP))
|
|
|
|
{ parse_directive(TRUE);
|
|
|
|
parse_statement(break_label, continue_label); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == AT_SEP))
|
|
|
|
{ parse_assembly(); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
|
|
|
|
|
|
|
|
if (token_type == DQ_TT)
|
|
|
|
{ parse_print_g(TRUE); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((token_type == SEP_TT) && (token_value == LESS_SEP))
|
|
|
|
{ parse_action(); goto StatementTerminator; }
|
|
|
|
|
|
|
|
if (token_type == EOF_TT)
|
|
|
|
{ ebf_error("statement", token_text); return; }
|
|
|
|
|
|
|
|
if (token_type != STATEMENT_TT)
|
|
|
|
{ put_token_back();
|
|
|
|
AO = parse_expression(VOID_CONTEXT);
|
|
|
|
code_generate(AO, VOID_CONTEXT, -1);
|
|
|
|
if (vivc_flag) { panic_mode_error_recovery(); return; }
|
|
|
|
goto StatementTerminator;
|
|
|
|
}
|
|
|
|
|
|
|
|
statements.enabled = FALSE;
|
|
|
|
|
|
|
|
switch(token_value)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* box <string-1> ... <string-n> -------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case BOX_CODE:
|
|
|
|
INITAOT(&AO3, CONSTANT_OT);
|
|
|
|
AO3.value = begin_table_array();
|
|
|
|
AO3.marker = ARRAY_MV;
|
|
|
|
ln = 0; ln2 = 0;
|
|
|
|
do
|
|
|
|
{ get_next_token();
|
|
|
|
if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
|
|
|
|
break;
|
|
|
|
if (token_type != DQ_TT)
|
|
|
|
ebf_error("text of box line in double-quotes",
|
|
|
|
token_text);
|
|
|
|
{ int i, j;
|
|
|
|
for (i=0, j=0; token_text[i] != 0; j++)
|
|
|
|
if (token_text[i] == '@')
|
|
|
|
{ if (token_text[i+1] == '@')
|
|
|
|
{ i = i + 2;
|
|
|
|
while (isdigit(token_text[i])) i++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ i++;
|
|
|
|
if (token_text[i] != 0) i++;
|
|
|
|
if (token_text[i] != 0) i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else i++;
|
|
|
|
if (j > ln2) ln2 = j;
|
|
|
|
}
|
|
|
|
put_token_back();
|
2020-06-01 20:29:12 +03:00
|
|
|
array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
|
2019-02-05 02:44:07 +02:00
|
|
|
} while (TRUE);
|
2020-06-01 20:29:12 +03:00
|
|
|
finish_array(ln, FALSE);
|
2019-02-05 02:44:07 +02:00
|
|
|
if (ln == 0)
|
|
|
|
error("No lines of text given for 'box' display");
|
|
|
|
|
|
|
|
INITAO(&AO2);
|
|
|
|
AO2.value = ln2; set_constant_ot(&AO2);
|
|
|
|
assembleg_call_2(veneer_routine(Box__Routine_VR),
|
|
|
|
AO2, AO3, zero_operand);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* break -------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case BREAK_CODE:
|
|
|
|
if (break_label == -1)
|
|
|
|
error("'break' can only be used in a loop or 'switch' block");
|
|
|
|
else
|
|
|
|
assembleg_jump(break_label);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* continue ----------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case CONTINUE_CODE:
|
|
|
|
if (continue_label == -1)
|
|
|
|
error("'continue' can only be used in a loop block");
|
|
|
|
else
|
|
|
|
assembleg_jump(continue_label);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* do <codeblock> until (<condition>) --------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case DO_CODE:
|
|
|
|
assemble_label_no(ln = next_label++);
|
|
|
|
ln2 = next_label++; ln3 = next_label++;
|
|
|
|
parse_code_block(ln3, ln2, 0);
|
|
|
|
statements.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == STATEMENT_TT)
|
|
|
|
&& (token_value == UNTIL_CODE))
|
|
|
|
{ assemble_label_no(ln2);
|
|
|
|
match_open_bracket();
|
|
|
|
AO = parse_expression(CONDITION_CONTEXT);
|
|
|
|
match_close_bracket();
|
|
|
|
code_generate(AO, CONDITION_CONTEXT, ln);
|
|
|
|
}
|
|
|
|
else error("'do' without matching 'until'");
|
|
|
|
|
|
|
|
assemble_label_no(ln3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* font on/off -------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case FONT_CODE:
|
|
|
|
misc_keywords.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
if ((token_type != MISC_KEYWORD_TT)
|
|
|
|
|| ((token_value != ON_MK)
|
|
|
|
&& (token_value != OFF_MK)))
|
|
|
|
{ ebf_error("'on' or 'off'", token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Call glk_set_style(normal or preformatted) */
|
|
|
|
INITAO(&AO);
|
|
|
|
AO.value = 0x0086;
|
|
|
|
set_constant_ot(&AO);
|
|
|
|
if (token_value == ON_MK)
|
|
|
|
AO2 = zero_operand;
|
|
|
|
else
|
|
|
|
AO2 = two_operand;
|
|
|
|
assembleg_call_2(veneer_routine(Glk__Wrap_VR),
|
|
|
|
AO, AO2, zero_operand);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* for (<initialisation> : <continue-condition> : <updating>) --------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/* Note that it's legal for any or all of the three sections of a
|
|
|
|
'for' specification to be empty. This 'for' implementation
|
|
|
|
often wastes 3 bytes with a redundant branch rather than keep
|
|
|
|
expression parse trees for long periods (as previous versions
|
|
|
|
of Inform did, somewhat crudely by simply storing the textual
|
|
|
|
form of a 'for' loop). It is adequate for now. */
|
|
|
|
|
|
|
|
case FOR_CODE:
|
|
|
|
match_open_bracket();
|
|
|
|
get_next_token();
|
|
|
|
|
|
|
|
/* Initialisation code */
|
2022-03-03 02:10:25 +02:00
|
|
|
AO.type = OMITTED_OT;
|
|
|
|
spare_debug_location1 = statement_debug_location;
|
|
|
|
AO2.type = OMITTED_OT; flag = 0;
|
|
|
|
spare_debug_location2 = statement_debug_location;
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
|
|
|
|
{ put_token_back();
|
|
|
|
if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
|
|
|
|
{ sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = get_token_location();
|
|
|
|
code_generate(parse_expression(FORINIT_CONTEXT),
|
|
|
|
VOID_CONTEXT, -1);
|
|
|
|
}
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
|
|
|
|
{ get_next_token();
|
|
|
|
if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
|
|
|
|
{ assemble_label_no(ln = next_label++);
|
|
|
|
ln2 = next_label++;
|
|
|
|
parse_code_block(ln2, ln, 0);
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
if (!execution_never_reaches_here)
|
|
|
|
assembleg_jump(ln);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
goto ParseUpdate;
|
|
|
|
}
|
|
|
|
put_token_back();
|
|
|
|
if (!match_colon()) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
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:
|
|
|
|
if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
|
|
|
|
{ put_token_back();
|
|
|
|
spare_debug_location2 = get_token_location();
|
|
|
|
AO2 = parse_expression(VOID_CONTEXT);
|
|
|
|
match_close_bracket();
|
|
|
|
flag = test_for_incdec(AO2);
|
|
|
|
}
|
|
|
|
|
|
|
|
ln = next_label++;
|
|
|
|
ln2 = next_label++;
|
|
|
|
ln3 = next_label++;
|
|
|
|
|
|
|
|
if ((AO2.type == OMITTED_OT) || (flag != 0))
|
|
|
|
{
|
|
|
|
assemble_label_no(ln);
|
|
|
|
if (flag==0) assemble_label_no(ln2);
|
|
|
|
|
|
|
|
/* The "finished yet?" condition */
|
|
|
|
|
|
|
|
if (AO.type != OMITTED_OT)
|
|
|
|
{ sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = spare_debug_location1;
|
|
|
|
code_generate(AO, CONDITION_CONTEXT, ln3);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* This is the jump which could be avoided with the aid
|
|
|
|
of long-term expression storage */
|
|
|
|
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
assembleg_jump(ln2);
|
|
|
|
|
|
|
|
/* The "update" part */
|
|
|
|
|
|
|
|
assemble_label_no(ln);
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = spare_debug_location2;
|
|
|
|
code_generate(AO2, VOID_CONTEXT, -1);
|
|
|
|
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
|
|
|
|
/* The "finished yet?" condition */
|
|
|
|
|
|
|
|
if (AO.type != OMITTED_OT)
|
|
|
|
{ sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = spare_debug_location1;
|
|
|
|
code_generate(AO, CONDITION_CONTEXT, ln3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flag != 0)
|
|
|
|
{
|
|
|
|
/* In this optimised case, update code is at the end
|
|
|
|
of the loop block, so "continue" goes there */
|
|
|
|
|
|
|
|
parse_code_block(ln3, ln2, 0);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
statement_debug_location = spare_debug_location2;
|
|
|
|
if (flag > 0)
|
|
|
|
{ INITAO(&AO3);
|
|
|
|
AO3.value = flag;
|
|
|
|
if (AO3.value >= MAX_LOCAL_VARIABLES)
|
|
|
|
AO3.type = GLOBALVAR_OT;
|
|
|
|
else
|
|
|
|
AO3.type = LOCALVAR_OT;
|
|
|
|
assembleg_3(add_gc, AO3, one_operand, AO3);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ INITAO(&AO3);
|
|
|
|
AO3.value = -flag;
|
|
|
|
if (AO3.value >= MAX_LOCAL_VARIABLES)
|
|
|
|
AO3.type = GLOBALVAR_OT;
|
|
|
|
else
|
|
|
|
AO3.type = LOCALVAR_OT;
|
|
|
|
assembleg_3(sub_gc, AO3, one_operand, AO3);
|
|
|
|
}
|
|
|
|
assembleg_jump(ln);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* In the unoptimised case, update code is at the
|
|
|
|
start of the loop block, so "continue" goes there */
|
|
|
|
|
|
|
|
parse_code_block(ln3, ln, 0);
|
|
|
|
if (!execution_never_reaches_here)
|
|
|
|
{ sequence_point_follows = FALSE;
|
|
|
|
assembleg_jump(ln);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assemble_label_no(ln3);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case GIVE_CODE:
|
|
|
|
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
|
2019-02-05 02:44:07 +02:00
|
|
|
if ((AO.type == LOCALVAR_OT) && (AO.value == 0))
|
|
|
|
onstack = TRUE;
|
|
|
|
else
|
|
|
|
onstack = FALSE;
|
|
|
|
|
|
|
|
do
|
|
|
|
{ get_next_token();
|
|
|
|
if ((token_type == SEP_TT)
|
|
|
|
&& (token_value == SEMICOLON_SEP)) {
|
|
|
|
if (onstack) {
|
|
|
|
assembleg_2(copy_gc, stack_pointer, zero_operand);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
|
|
|
|
ln = 0;
|
|
|
|
else
|
2022-03-03 02:10:25 +02:00
|
|
|
{ ln = 1;
|
2019-02-05 02:44:07 +02:00
|
|
|
put_token_back();
|
|
|
|
}
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
|
2019-02-05 02:44:07 +02:00
|
|
|
if (runtime_error_checking_switch && (!veneer_mode))
|
|
|
|
{ ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR);
|
|
|
|
if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
|
|
|
|
/* already on stack */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assembleg_store(stack_pointer, AO2);
|
|
|
|
}
|
|
|
|
if (onstack)
|
|
|
|
assembleg_2(stkpeek_gc, one_operand, stack_pointer);
|
|
|
|
else
|
|
|
|
assembleg_store(stack_pointer, AO);
|
|
|
|
assembleg_3(call_gc, veneer_routine(ln2), two_operand,
|
|
|
|
zero_operand);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (is_constant_ot(AO2.type) && AO2.marker == 0) {
|
|
|
|
AO2.value += 8;
|
|
|
|
set_constant_ot(&AO2);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
INITAOTV(&AO3, BYTECONSTANT_OT, 8);
|
|
|
|
assembleg_3(add_gc, AO2, AO3, stack_pointer);
|
|
|
|
AO2 = stack_pointer;
|
|
|
|
}
|
|
|
|
if (onstack) {
|
|
|
|
if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0))
|
|
|
|
assembleg_2(stkpeek_gc, one_operand,
|
|
|
|
stack_pointer);
|
|
|
|
else
|
|
|
|
assembleg_2(stkpeek_gc, zero_operand,
|
|
|
|
stack_pointer);
|
|
|
|
}
|
|
|
|
if (ln)
|
|
|
|
AO3 = one_operand;
|
|
|
|
else
|
|
|
|
AO3 = zero_operand;
|
|
|
|
assembleg_3(astorebit_gc, AO, AO2, AO3);
|
|
|
|
}
|
|
|
|
} while(TRUE);
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* if (<condition>) <codeblock> [else <codeblock>] -------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case IF_CODE:
|
|
|
|
flag = FALSE;
|
|
|
|
ln2 = 0;
|
|
|
|
|
|
|
|
match_open_bracket();
|
|
|
|
AO = parse_expression(CONDITION_CONTEXT);
|
|
|
|
match_close_bracket();
|
|
|
|
|
|
|
|
statements.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
|
|
|
|
ln = -4;
|
|
|
|
else
|
|
|
|
if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
|
|
|
|
ln = -3;
|
|
|
|
else
|
|
|
|
{ put_token_back();
|
|
|
|
ln = next_label++;
|
|
|
|
}
|
|
|
|
|
|
|
|
code_generate(AO, CONDITION_CONTEXT, ln);
|
|
|
|
|
|
|
|
if (ln >= 0) parse_code_block(break_label, continue_label, 0);
|
|
|
|
else
|
|
|
|
{ get_next_token();
|
|
|
|
if ((token_type != SEP_TT)
|
|
|
|
|| (token_value != SEMICOLON_SEP))
|
|
|
|
{ ebf_error("';'", token_text);
|
|
|
|
put_token_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
statements.enabled = TRUE;
|
|
|
|
get_next_token();
|
2022-03-03 02:10:25 +02:00
|
|
|
|
|
|
|
/* An #if directive around the ELSE clause is legal. */
|
|
|
|
while ((token_type == SEP_TT) && (token_value == HASH_SEP))
|
|
|
|
{ parse_directive(TRUE);
|
|
|
|
statements.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
}
|
|
|
|
|
2019-02-05 02:44:07 +02:00
|
|
|
if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
|
|
|
|
{ flag = TRUE;
|
|
|
|
if (ln >= 0)
|
|
|
|
{ ln2 = next_label++;
|
|
|
|
if (!execution_never_reaches_here)
|
|
|
|
{ sequence_point_follows = FALSE;
|
|
|
|
assembleg_jump(ln2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else put_token_back();
|
|
|
|
|
|
|
|
if (ln >= 0) assemble_label_no(ln);
|
|
|
|
|
|
|
|
if (flag)
|
|
|
|
{ parse_code_block(break_label, continue_label, 0);
|
|
|
|
if (ln >= 0) assemble_label_no(ln2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* inversion ---------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case INVERSION_CODE:
|
|
|
|
INITAOTV(&AO2, DEREFERENCE_OT, GLULX_HEADER_SIZE+8);
|
|
|
|
assembleg_2(copyb_gc, AO2, stack_pointer);
|
|
|
|
assembleg_1(streamchar_gc, stack_pointer);
|
|
|
|
AO2.value = GLULX_HEADER_SIZE+9;
|
|
|
|
assembleg_2(copyb_gc, AO2, stack_pointer);
|
|
|
|
assembleg_1(streamchar_gc, stack_pointer);
|
|
|
|
AO2.value = GLULX_HEADER_SIZE+10;
|
|
|
|
assembleg_2(copyb_gc, AO2, stack_pointer);
|
|
|
|
assembleg_1(streamchar_gc, stack_pointer);
|
|
|
|
AO2.value = GLULX_HEADER_SIZE+11;
|
|
|
|
assembleg_2(copyb_gc, AO2, stack_pointer);
|
|
|
|
assembleg_1(streamchar_gc, stack_pointer);
|
|
|
|
|
|
|
|
if (/* DISABLES CODE */ (0)) {
|
|
|
|
INITAO(&AO);
|
|
|
|
AO.value = '(';
|
|
|
|
set_constant_ot(&AO);
|
|
|
|
assembleg_1(streamchar_gc, AO);
|
|
|
|
AO.value = 'G';
|
|
|
|
set_constant_ot(&AO);
|
|
|
|
assembleg_1(streamchar_gc, AO);
|
|
|
|
|
|
|
|
AO2.value = GLULX_HEADER_SIZE+12;
|
|
|
|
assembleg_2(copyb_gc, AO2, stack_pointer);
|
|
|
|
assembleg_1(streamchar_gc, stack_pointer);
|
|
|
|
AO2.value = GLULX_HEADER_SIZE+13;
|
|
|
|
assembleg_2(copyb_gc, AO2, stack_pointer);
|
|
|
|
assembleg_1(streamchar_gc, stack_pointer);
|
|
|
|
AO2.value = GLULX_HEADER_SIZE+14;
|
|
|
|
assembleg_2(copyb_gc, AO2, stack_pointer);
|
|
|
|
assembleg_1(streamchar_gc, stack_pointer);
|
|
|
|
AO2.value = GLULX_HEADER_SIZE+15;
|
|
|
|
assembleg_2(copyb_gc, AO2, stack_pointer);
|
|
|
|
assembleg_1(streamchar_gc, stack_pointer);
|
|
|
|
|
|
|
|
AO.marker = 0;
|
|
|
|
AO.value = ')';
|
|
|
|
set_constant_ot(&AO);
|
|
|
|
assembleg_1(streamchar_gc, AO);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* jump <label> ------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case JUMP_CODE:
|
|
|
|
assembleg_jump(parse_label());
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* move <expression> to <expression> ---------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case MOVE_CODE:
|
|
|
|
misc_keywords.enabled = TRUE;
|
|
|
|
AO = parse_expression(QUANTITY_CONTEXT);
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
if ((token_type != MISC_KEYWORD_TT)
|
|
|
|
|| (token_value != TO_MK))
|
|
|
|
{ ebf_error("'to'", token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
AO = code_generate(AO, QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
|
|
|
|
check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
|
2019-02-05 02:44:07 +02:00
|
|
|
if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
|
|
|
|
assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2,
|
|
|
|
zero_operand);
|
|
|
|
else
|
|
|
|
assembleg_call_2(veneer_routine(OB__Move_VR), AO, AO2,
|
|
|
|
zero_operand);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* new_line ----------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case NEW_LINE_CODE:
|
|
|
|
INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
|
|
|
|
assembleg_1(streamchar_gc, AO);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* objectloop (<initialisation>) <codeblock> -------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case OBJECTLOOP_CODE:
|
|
|
|
|
|
|
|
match_open_bracket();
|
|
|
|
get_next_token();
|
|
|
|
if (token_type == LOCAL_VARIABLE_TT) {
|
|
|
|
INITAOTV(&AO, LOCALVAR_OT, token_value);
|
|
|
|
}
|
|
|
|
else if ((token_type == SYMBOL_TT) &&
|
2022-03-03 02:10:25 +02:00
|
|
|
(symbols[token_value].type == GLOBAL_VARIABLE_T)) {
|
|
|
|
INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
ebf_error("'objectloop' variable", token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
misc_keywords.enabled = TRUE;
|
|
|
|
get_next_token(); flag = TRUE;
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
|
|
|
|
flag = FALSE;
|
|
|
|
|
|
|
|
ln = 0;
|
|
|
|
if ((token_type == MISC_KEYWORD_TT)
|
|
|
|
&& (token_value == NEAR_MK)) ln = 1;
|
|
|
|
if ((token_type == MISC_KEYWORD_TT)
|
|
|
|
&& (token_value == FROM_MK)) ln = 2;
|
|
|
|
if ((token_type == CND_TT) && (token_value == IN_COND))
|
|
|
|
{ get_next_token();
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
|
|
|
|
ln = 3;
|
|
|
|
put_token_back();
|
|
|
|
put_token_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ln != 0) {
|
|
|
|
/* Old style (Inform 5) objectloops: note that we
|
|
|
|
implement objectloop (a in b) in the old way since
|
|
|
|
this runs through objects in a different order from
|
|
|
|
the new way, and there may be existing Inform code
|
|
|
|
relying on this. */
|
|
|
|
assembly_operand AO4, AO5;
|
|
|
|
INITAO(&AO5);
|
|
|
|
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
match_close_bracket();
|
|
|
|
if (ln == 1) {
|
|
|
|
if (runtime_error_checking_switch)
|
|
|
|
AO2 = check_nonzero_at_runtime(AO2, -1,
|
|
|
|
OBJECTLOOP_RTE);
|
|
|
|
INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
|
|
|
|
assembleg_3(aload_gc, AO2, AO4, stack_pointer);
|
|
|
|
INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
|
|
|
|
assembleg_3(aload_gc, stack_pointer, AO4, stack_pointer);
|
|
|
|
AO2 = stack_pointer;
|
|
|
|
}
|
|
|
|
else if (ln == 3) {
|
|
|
|
if (runtime_error_checking_switch) {
|
|
|
|
AO5 = AO2;
|
|
|
|
AO2 = check_nonzero_at_runtime(AO2, -1,
|
|
|
|
CHILD_RTE);
|
|
|
|
}
|
|
|
|
INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
|
|
|
|
assembleg_3(aload_gc, AO2, AO4, stack_pointer);
|
|
|
|
AO2 = stack_pointer;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
assembleg_store(AO, AO2);
|
|
|
|
assembleg_1_branch(jz_gc, AO, ln2 = next_label++);
|
|
|
|
assemble_label_no(ln4 = next_label++);
|
|
|
|
parse_code_block(ln2, ln3 = next_label++, 0);
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
assemble_label_no(ln3);
|
|
|
|
if (runtime_error_checking_switch) {
|
|
|
|
AO2 = check_nonzero_at_runtime(AO, ln2,
|
|
|
|
OBJECTLOOP2_RTE);
|
|
|
|
if ((ln == 3)
|
|
|
|
&& ((AO5.type != LOCALVAR_OT)||(AO5.value != 0))
|
|
|
|
&& ((AO5.type != LOCALVAR_OT)||(AO5.value != AO.value)))
|
|
|
|
{ assembly_operand en_ao;
|
|
|
|
INITAO(&en_ao);
|
|
|
|
en_ao.value = OBJECTLOOP_BROKEN_RTE;
|
|
|
|
set_constant_ot(&en_ao);
|
|
|
|
INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
|
|
|
|
assembleg_3(aload_gc, AO, AO4, stack_pointer);
|
|
|
|
assembleg_2_branch(jeq_gc, stack_pointer, AO5,
|
|
|
|
next_label);
|
|
|
|
assembleg_call_2(veneer_routine(RT__Err_VR),
|
|
|
|
en_ao, AO, zero_operand);
|
|
|
|
assembleg_jump(ln2);
|
|
|
|
assemble_label_no(next_label++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
AO2 = AO;
|
|
|
|
}
|
|
|
|
INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_SIBLING());
|
|
|
|
assembleg_3(aload_gc, AO2, AO4, AO);
|
|
|
|
assembleg_1_branch(jnz_gc, AO, ln4);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
ln = symbol_index("Class", -1);
|
|
|
|
INITAOT(&AO2, CONSTANT_OT);
|
2022-03-03 02:10:25 +02:00
|
|
|
AO2.value = symbols[ln].value;
|
2019-02-05 02:44:07 +02:00
|
|
|
AO2.marker = OBJECT_MV;
|
|
|
|
assembleg_store(AO, AO2);
|
|
|
|
|
|
|
|
assemble_label_no(ln = next_label++);
|
|
|
|
ln2 = next_label++;
|
|
|
|
ln3 = next_label++;
|
|
|
|
if (flag)
|
|
|
|
{ put_token_back();
|
|
|
|
put_token_back();
|
|
|
|
sequence_point_follows = TRUE;
|
|
|
|
code_generate(parse_expression(CONDITION_CONTEXT),
|
|
|
|
CONDITION_CONTEXT, ln3);
|
|
|
|
match_close_bracket();
|
|
|
|
}
|
|
|
|
parse_code_block(ln2, ln3, 0);
|
|
|
|
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
assemble_label_no(ln3);
|
|
|
|
INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHAIN());
|
|
|
|
assembleg_3(aload_gc, AO, AO4, AO);
|
|
|
|
assembleg_1_branch(jnz_gc, AO, ln);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* (see routine above) ------------------------------------------------ */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case PRINT_CODE:
|
|
|
|
get_next_token();
|
|
|
|
parse_print_g(FALSE); return;
|
|
|
|
case PRINT_RET_CODE:
|
|
|
|
get_next_token();
|
|
|
|
parse_print_g(TRUE); return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* quit --------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case QUIT_CODE:
|
|
|
|
assembleg_0(quit_gc); break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* remove <expression> ------------------------------------------------ */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case REMOVE_CODE:
|
|
|
|
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
|
2019-02-05 02:44:07 +02:00
|
|
|
if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
|
|
|
|
assembleg_call_1(veneer_routine(RT__ChR_VR), AO,
|
|
|
|
zero_operand);
|
|
|
|
else
|
|
|
|
assembleg_call_1(veneer_routine(OB__Remove_VR), AO,
|
|
|
|
zero_operand);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* return [<expression>] ---------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case RETURN_CODE:
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) {
|
|
|
|
assembleg_1(return_gc, one_operand);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
put_token_back();
|
|
|
|
AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
assembleg_1(return_gc, AO);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* rfalse ------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case RFALSE_CODE:
|
|
|
|
assembleg_1(return_gc, zero_operand);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* rtrue -------------------------------------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case RTRUE_CODE:
|
|
|
|
assembleg_1(return_gc, one_operand);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* spaces <expression> ------------------------------------------------ */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case SPACES_CODE:
|
|
|
|
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
|
|
|
|
assembleg_store(temp_var1, AO);
|
|
|
|
|
|
|
|
INITAO(&AO);
|
|
|
|
AO.value = 32; set_constant_ot(&AO);
|
|
|
|
|
|
|
|
assembleg_2_branch(jlt_gc, temp_var1, one_operand,
|
|
|
|
ln = next_label++);
|
|
|
|
assemble_label_no(ln2 = next_label++);
|
|
|
|
assembleg_1(streamchar_gc, AO);
|
|
|
|
assembleg_dec(temp_var1);
|
|
|
|
assembleg_1_branch(jnz_gc, temp_var1, ln2);
|
|
|
|
assemble_label_no(ln);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* string <expression> <literal-string> ------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case STRING_CODE:
|
|
|
|
AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
2022-03-03 02:10:25 +02:00
|
|
|
if (is_constant_ot(AO2.type) && AO2.marker == 0) {
|
|
|
|
/* Compile-time check */
|
|
|
|
if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
|
|
|
|
error_max_dynamic_strings(AO2.value);
|
|
|
|
}
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
get_next_token();
|
|
|
|
if (token_type == DQ_TT)
|
|
|
|
{ INITAOT(&AO4, CONSTANT_OT);
|
2022-03-03 02:10:25 +02:00
|
|
|
/* 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);
|
2019-02-05 02:44:07 +02:00
|
|
|
AO4.marker = STRING_MV;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ put_token_back();
|
|
|
|
AO4 = parse_expression(CONSTANT_CONTEXT);
|
|
|
|
}
|
|
|
|
assembleg_call_2(veneer_routine(Dynam__String_VR),
|
|
|
|
AO2, AO4, zero_operand);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* style roman/reverse/bold/underline/fixed --------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case STYLE_CODE:
|
|
|
|
misc_keywords.enabled = TRUE;
|
|
|
|
get_next_token();
|
|
|
|
misc_keywords.enabled = FALSE;
|
|
|
|
if ((token_type != MISC_KEYWORD_TT)
|
|
|
|
|| ((token_value != ROMAN_MK)
|
|
|
|
&& (token_value != REVERSE_MK)
|
|
|
|
&& (token_value != BOLD_MK)
|
|
|
|
&& (token_value != UNDERLINE_MK)
|
|
|
|
&& (token_value != FIXED_MK)))
|
|
|
|
{ ebf_error(
|
|
|
|
"'roman', 'bold', 'underline', 'reverse' or 'fixed'",
|
|
|
|
token_text);
|
|
|
|
panic_mode_error_recovery();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Call glk_set_style() */
|
|
|
|
|
|
|
|
INITAO(&AO);
|
|
|
|
AO.value = 0x0086;
|
|
|
|
set_constant_ot(&AO);
|
|
|
|
switch(token_value)
|
|
|
|
{ case ROMAN_MK:
|
|
|
|
default:
|
|
|
|
AO2 = zero_operand; /* normal */
|
|
|
|
break;
|
|
|
|
case REVERSE_MK:
|
|
|
|
INITAO(&AO2);
|
|
|
|
AO2.value = 5; /* alert */
|
|
|
|
set_constant_ot(&AO2);
|
|
|
|
break;
|
|
|
|
case BOLD_MK:
|
|
|
|
INITAO(&AO2);
|
|
|
|
AO2.value = 4; /* subheader */
|
|
|
|
set_constant_ot(&AO2);
|
|
|
|
break;
|
|
|
|
case UNDERLINE_MK:
|
|
|
|
AO2 = one_operand; /* emphasized */
|
|
|
|
break;
|
|
|
|
case FIXED_MK:
|
|
|
|
AO2 = two_operand; /* preformatted */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assembleg_call_2(veneer_routine(Glk__Wrap_VR),
|
|
|
|
AO, AO2, zero_operand);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* switch (<expression>) <codeblock> ---------------------------------- */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case SWITCH_CODE:
|
|
|
|
match_open_bracket();
|
|
|
|
AO = code_generate(parse_expression(QUANTITY_CONTEXT),
|
|
|
|
QUANTITY_CONTEXT, -1);
|
|
|
|
match_close_bracket();
|
|
|
|
|
|
|
|
assembleg_store(temp_var1, AO);
|
|
|
|
|
|
|
|
parse_code_block(ln = next_label++, continue_label, 1);
|
|
|
|
assemble_label_no(ln);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* while (<condition>) <codeblock> ------------------------------------ */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case WHILE_CODE:
|
|
|
|
assemble_label_no(ln = next_label++);
|
|
|
|
match_open_bracket();
|
|
|
|
|
|
|
|
code_generate(parse_expression(CONDITION_CONTEXT),
|
|
|
|
CONDITION_CONTEXT, ln2 = next_label++);
|
|
|
|
match_close_bracket();
|
|
|
|
|
|
|
|
parse_code_block(ln2, ln, 0);
|
|
|
|
sequence_point_follows = FALSE;
|
|
|
|
assembleg_jump(ln);
|
|
|
|
assemble_label_no(ln2);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
case SDEFAULT_CODE:
|
|
|
|
error("'default' without matching 'switch'"); break;
|
|
|
|
case ELSE_CODE:
|
|
|
|
error("'else' without matching 'if'"); break;
|
|
|
|
case UNTIL_CODE:
|
|
|
|
error("'until' without matching 'do'");
|
|
|
|
panic_mode_error_recovery(); return;
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/* And a useful default, which will never be triggered in a complete
|
|
|
|
Inform compiler, but which is important in development. */
|
|
|
|
|
|
|
|
default:
|
|
|
|
error("*** Statement code gen: Can't generate yet ***\n");
|
|
|
|
panic_mode_error_recovery(); return;
|
|
|
|
}
|
|
|
|
|
|
|
|
StatementTerminator:
|
|
|
|
|
|
|
|
get_next_token();
|
|
|
|
if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
|
|
|
|
{ ebf_error("';'", token_text);
|
|
|
|
put_token_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void parse_statement(int break_label, int continue_label)
|
|
|
|
{
|
|
|
|
if (!glulx_mode)
|
|
|
|
parse_statement_z(break_label, continue_label);
|
|
|
|
else
|
|
|
|
parse_statement_g(break_label, continue_label);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ========================================================================= */
|
|
|
|
/* Data structure management routines */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
extern void init_states_vars(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void states_begin_pass(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void states_allocate_arrays(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void states_free_arrays(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ========================================================================= */
|