1 /* ------------------------------------------------------------------------- */
2 /* "states" : Statement translator */
4 /* Part of Inform 6.42 */
5 /* copyright (c) Graham Nelson 1993 - 2024 */
7 /* Inform is free software: you can redistribute it and/or modify */
8 /* it under the terms of the GNU General Public License as published by */
9 /* the Free Software Foundation, either version 3 of the License, or */
10 /* (at your option) any later version. */
12 /* Inform is distributed in the hope that it will be useful, */
13 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
14 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
15 /* GNU General Public License for more details. */
17 /* You should have received a copy of the GNU General Public License */
18 /* along with Inform. If not, see https://gnu.org/licenses/ */
20 /* ------------------------------------------------------------------------- */
24 static int match_colon(void)
26 if (token_type == SEP_TT)
27 { if (token_value == SEMICOLON_SEP)
28 warning("Unlike C, Inform uses ':' to divide parts \
29 of a 'for' loop specification: replacing ';' with ':'");
31 if (token_value != COLON_SEP)
32 { ebf_curtoken_error("':'");
33 panic_mode_error_recovery();
38 { ebf_curtoken_error("':'");
39 panic_mode_error_recovery();
45 static void match_open_bracket(void)
47 if ((token_type == SEP_TT) && (token_value == OPENB_SEP)) return;
49 ebf_curtoken_error("'('");
52 extern void match_close_bracket(void)
54 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP)) return;
56 ebf_curtoken_error("')'");
59 static void parse_action(void)
60 { int level = 1, args = 0, codegen_action;
61 assembly_operand AO, AO2, AO3, AO4, AO5;
63 /* An action statement has the form <ACTION NOUN SECOND, ACTOR>
64 or <<ACTION NOUN SECOND, ACTOR>>. It simply compiles into a call
65 to R_Process() with those four arguments. (The latter form,
66 with double brackets, means "return true afterwards".)
68 The R_Process() function should be supplied by the library,
69 although a stub is defined in the veneer.
71 The NOUN, SECOND, and ACTOR arguments are optional. If not
72 supplied, R_Process() will be called with fewer arguments.
73 (But if you supply ACTOR, it must be preceded by a comma.
74 <ACTION, ACTOR> is equivalent to <ACTION 0 0, ACTOR>.)
76 To complicate life, the ACTION argument may be a bare action
77 name or a parenthesized expression. (So <Take> is equivalent
78 to <(##Take)>.) We have to peek at the first token, checking
79 whether it's an open-paren, to distinguish these cases.
81 You may ask why the ACTOR argument is last; the "natural"
82 Inform ordering would be "<floyd, take ball>". True! Sadly,
83 Inform's lexer isn't smart enough to parse this consistently,
87 dont_enter_into_symbol_table = TRUE;
89 if ((token_type == SEP_TT) && (token_value == LESS_SEP))
90 { level = 2; get_next_token();
92 dont_enter_into_symbol_table = FALSE;
94 /* Peek at the next token; see if it's an open-paren. */
95 if ((token_type==SEP_TT) && (token_value==OPENB_SEP))
97 AO2 = parse_expression(ACTION_Q_CONTEXT);
98 codegen_action = TRUE;
102 if (token_type != UQ_TT) {
103 ebf_curtoken_error("name of action");
105 codegen_action = FALSE;
106 AO2 = action_of_name(token_text);
113 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
116 AO3 = parse_expression(ACTION_Q_CONTEXT);
120 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
123 AO4 = parse_expression(QUANTITY_CONTEXT);
126 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
128 ebf_curtoken_error("',' or '>'");
131 if ((token_type == SEP_TT) && (token_value == COMMA_SEP))
133 if (!glulx_mode && (version_number < 4))
135 error("<x, y> syntax is not available in Z-code V3 or earlier");
138 AO5 = parse_expression(QUANTITY_CONTEXT);
140 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
142 ebf_curtoken_error("'>'");
148 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
150 ebf_curtoken_error("'>>'");
156 AO = veneer_routine(R_Process_VR);
160 if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
161 if (version_number>=5)
162 assemblez_2(call_2n_zc, AO, AO2);
164 if (version_number==4)
165 assemblez_2_to(call_vs_zc, AO, AO2, temp_var1);
167 assemblez_2_to(call_zc, AO, AO2, temp_var1);
170 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
171 if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
172 if (version_number>=5)
173 assemblez_3(call_vn_zc, AO, AO2, AO3);
175 if (version_number==4)
176 assemblez_3_to(call_vs_zc, AO, AO2, AO3, temp_var1);
178 assemblez_3_to(call_zc, AO, AO2, AO3, temp_var1);
181 AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
182 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
183 if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
184 if (version_number>=5)
185 assemblez_4(call_vn_zc, AO, AO2, AO3, AO4);
187 if (version_number==4)
188 assemblez_4_to(call_vs_zc, AO, AO2, AO3, AO4, temp_var1);
190 assemblez_4_to(call_zc, AO, AO2, AO3, AO4, temp_var1);
193 AO5 = code_generate(AO5, QUANTITY_CONTEXT, -1);
194 AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
195 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
196 if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
197 if (version_number>=5)
198 assemblez_5(call_vn2_zc, AO, AO2, AO3, AO4, AO5);
200 if (version_number==4)
201 assemblez_5_to(call_vs2_zc, AO, AO2, AO3, AO4, AO5, temp_var1);
202 /* if V3 or earlier, we've already displayed an error */
207 if (level == 2) assemblez_0(rtrue_zc);
212 AO = veneer_routine(R_Process_VR);
218 AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
219 assembleg_call_1(AO, AO2, zero_operand);
223 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
225 AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
226 assembleg_call_2(AO, AO2, AO3, zero_operand);
230 AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
231 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
233 AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
234 assembleg_call_3(AO, AO2, AO3, AO4, zero_operand);
238 AO5 = code_generate(AO5, QUANTITY_CONTEXT, -1);
239 if (!((AO5.type == LOCALVAR_OT) && (AO5.value == 0)))
240 assembleg_store(stack_pointer, AO5);
241 AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
242 if (!((AO4.type == LOCALVAR_OT) && (AO4.value == 0)))
243 assembleg_store(stack_pointer, AO4);
244 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
245 if (!((AO3.type == LOCALVAR_OT) && (AO3.value == 0)))
246 assembleg_store(stack_pointer, AO3);
248 AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
249 if (!((AO2.type == LOCALVAR_OT) && (AO2.value == 0)))
250 assembleg_store(stack_pointer, AO2);
251 assembleg_3(call_gc, AO, four_operand, zero_operand);
256 assembleg_1(return_gc, one_operand);
261 extern int parse_label(void)
265 if ((token_type == SYMBOL_TT) &&
266 (symbols[token_value].type == LABEL_T))
267 { symbols[token_value].flags |= USED_SFLAG;
268 return(symbols[token_value].value);
271 if ((token_type == SYMBOL_TT) && (symbols[token_value].flags & UNKNOWN_SFLAG))
272 { assign_symbol(token_value, next_label, LABEL_T);
273 define_symbol_label(token_value);
275 symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG;
276 return(symbols[token_value].value);
279 ebf_curtoken_error("label name");
283 static void parse_print_z(int finally_return)
284 { int count = 0; assembly_operand AO;
286 /* print <printlist> -------------------------------------------------- */
287 /* print_ret <printlist> ---------------------------------------------- */
288 /* <literal-string> --------------------------------------------------- */
290 /* <printlist> is a comma-separated list of items: */
292 /* <literal-string> */
293 /* <other-expression> */
294 /* (char) <expression> */
295 /* (address) <expression> */
296 /* (string) <expression> */
297 /* (a) <expression> */
298 /* (the) <expression> */
299 /* (The) <expression> */
300 /* (name) <expression> */
301 /* (number) <expression> */
302 /* (property) <expression> */
303 /* (<routine>) <expression> */
304 /* (object) <expression> (for use in low-level code only) */
305 /* --------------------------------------------------------------------- */
308 { AI.text = token_text;
309 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
312 if (token_text[0] == '^' && token_text[1] == '\0') {
313 /* The string "^" is always a simple newline. */
314 assemblez_0(new_line_zc);
317 if ((int)strlen(token_text) > ZCODE_MAX_INLINE_STRING)
318 { INITAOT(&AO, LONG_CONSTANT_OT);
319 AO.marker = STRING_MV;
320 AO.value = compile_string(token_text, STRCTX_GAME);
321 assemblez_1(print_paddr_zc, AO);
324 if ((token_type == SEP_TT)
325 && (token_value == SEMICOLON_SEP))
326 { assemblez_0(new_line_zc);
327 assemblez_0(rtrue_zc);
336 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
337 { assemblez_0(print_ret_zc); return;
341 assemblez_0(print_zc);
345 if (token_value == OPENB_SEP)
346 { misc_keywords.enabled = TRUE;
349 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
350 { assembly_operand AO1;
352 put_token_back(); put_token_back();
353 local_variables.enabled = FALSE;
355 misc_keywords.enabled = FALSE;
356 local_variables.enabled = TRUE;
358 if ((token_type == STATEMENT_TT)
359 &&(token_value == STRING_CODE))
360 { token_type = MISC_KEYWORD_TT;
361 token_value = STRING_MK;
366 case MISC_KEYWORD_TT:
369 if (runtime_error_checking_switch)
370 { AO = veneer_routine(RT__ChPrintC_VR);
375 parse_expression(QUANTITY_CONTEXT),
376 QUANTITY_CONTEXT, -1);
377 assemblez_1(print_char_zc, AO1);
380 if (runtime_error_checking_switch)
381 { AO = veneer_routine(RT__ChPrintA_VR);
386 parse_expression(QUANTITY_CONTEXT),
387 QUANTITY_CONTEXT, -1);
388 assemblez_1(print_addr_zc, AO1);
391 if (runtime_error_checking_switch)
392 { AO = veneer_routine(RT__ChPrintS_VR);
397 parse_expression(QUANTITY_CONTEXT),
398 QUANTITY_CONTEXT, -1);
399 assemblez_1(print_paddr_zc, AO1);
402 if (runtime_error_checking_switch)
403 { AO = veneer_routine(RT__ChPrintO_VR);
408 parse_expression(QUANTITY_CONTEXT),
409 QUANTITY_CONTEXT, -1);
410 assemblez_1(print_obj_zc, AO1);
413 AO = veneer_routine(DefArt_VR);
417 AO = veneer_routine(InDefArt_VR);
420 AO = veneer_routine(CDefArt_VR);
423 AO = veneer_routine(CInDefArt_VR);
426 AO = veneer_routine(PrintShortName_VR);
429 AO = veneer_routine(EnglishNumber_VR);
432 AO = veneer_routine(Print__Pname_VR);
435 error_named("A reserved word was used as a print specification:",
441 if (symbols[token_value].flags & UNKNOWN_SFLAG)
442 { INITAOT(&AO, LONG_CONSTANT_OT);
443 AO.value = token_value;
444 AO.marker = SYMBOL_MV;
445 AO.symindex = token_value;
448 { INITAOT(&AO, LONG_CONSTANT_OT);
449 AO.value = symbols[token_value].value;
450 AO.marker = IROUTINE_MV;
451 AO.symindex = token_value;
452 if (symbols[token_value].type != ROUTINE_T)
453 ebf_curtoken_error("printing routine name");
455 symbols[token_value].flags |= USED_SFLAG;
460 if (version_number >= 5)
461 assemblez_2(call_2n_zc, AO,
462 code_generate(parse_expression(QUANTITY_CONTEXT),
463 QUANTITY_CONTEXT, -1));
464 else if (version_number == 4)
465 assemblez_2_to(call_vs_zc, AO,
466 code_generate(parse_expression(QUANTITY_CONTEXT),
467 QUANTITY_CONTEXT, -1), temp_var1);
469 assemblez_2_to(call_zc, AO,
470 code_generate(parse_expression(QUANTITY_CONTEXT),
471 QUANTITY_CONTEXT, -1), temp_var1);
474 default: ebf_curtoken_error("print specification");
476 assemblez_1(print_num_zc,
477 code_generate(parse_expression(QUANTITY_CONTEXT),
478 QUANTITY_CONTEXT, -1));
482 put_token_back(); put_token_back(); put_token_back();
483 misc_keywords.enabled = FALSE;
484 assemblez_1(print_num_zc,
485 code_generate(parse_expression(QUANTITY_CONTEXT),
486 QUANTITY_CONTEXT, -1));
491 put_token_back(); misc_keywords.enabled = FALSE;
492 assemblez_1(print_num_zc,
493 code_generate(parse_expression(QUANTITY_CONTEXT),
494 QUANTITY_CONTEXT, -1));
498 PrintTermDone: misc_keywords.enabled = FALSE;
502 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
503 if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
504 { ebf_curtoken_error("comma");
505 panic_mode_error_recovery(); return;
507 else get_next_token();
510 if (count == 0) ebf_curtoken_error("something to print");
512 { assemblez_0(new_line_zc);
513 assemblez_0(rtrue_zc);
517 static void parse_print_g(int finally_return)
518 { int count = 0; assembly_operand AO, AO2;
520 /* print <printlist> -------------------------------------------------- */
521 /* print_ret <printlist> ---------------------------------------------- */
522 /* <literal-string> --------------------------------------------------- */
524 /* <printlist> is a comma-separated list of items: */
526 /* <literal-string> */
527 /* <other-expression> */
528 /* (char) <expression> */
529 /* (address) <expression> */
530 /* (string) <expression> */
531 /* (a) <expression> */
532 /* (A) <expression> */
533 /* (the) <expression> */
534 /* (The) <expression> */
535 /* (name) <expression> */
536 /* (number) <expression> */
537 /* (property) <expression> */
538 /* (<routine>) <expression> */
539 /* (object) <expression> (for use in low-level code only) */
540 /* --------------------------------------------------------------------- */
544 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
547 if (token_text[0] == '^' && token_text[1] == '\0') {
548 /* The string "^" is always a simple newline. */
549 INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
550 assembleg_1(streamchar_gc, AO);
553 /* We can't compile a string into the instruction,
554 so this always goes into the string area. */
555 { INITAOT(&AO, CONSTANT_OT);
556 AO.marker = STRING_MV;
557 AO.value = compile_string(token_text, STRCTX_GAME);
558 assembleg_1(streamstr_gc, AO);
561 if ((token_type == SEP_TT)
562 && (token_value == SEMICOLON_SEP))
563 { INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
564 assembleg_1(streamchar_gc, AO);
565 INITAOTV(&AO, BYTECONSTANT_OT, 1);
566 assembleg_1(return_gc, AO);
576 if (token_value == OPENB_SEP)
577 { misc_keywords.enabled = TRUE;
580 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
581 { assembly_operand AO1;
583 put_token_back(); put_token_back();
584 local_variables.enabled = FALSE;
586 misc_keywords.enabled = FALSE;
587 local_variables.enabled = TRUE;
589 if ((token_type == STATEMENT_TT)
590 &&(token_value == STRING_CODE))
591 { token_type = MISC_KEYWORD_TT;
592 token_value = STRING_MK;
597 case MISC_KEYWORD_TT:
600 if (runtime_error_checking_switch)
601 { AO = veneer_routine(RT__ChPrintC_VR);
606 parse_expression(QUANTITY_CONTEXT),
607 QUANTITY_CONTEXT, -1);
608 if (is_constant_ot(AO1.type) && AO1.marker == 0) {
609 if (AO1.value >= 0 && AO1.value < 0x100)
610 assembleg_1(streamchar_gc, AO1);
612 assembleg_1(streamunichar_gc, AO1);
615 assembleg_1(streamunichar_gc, AO1);
619 if (runtime_error_checking_switch)
620 AO = veneer_routine(RT__ChPrintA_VR);
622 AO = veneer_routine(Print__Addr_VR);
625 if (runtime_error_checking_switch)
626 { AO = veneer_routine(RT__ChPrintS_VR);
631 parse_expression(QUANTITY_CONTEXT),
632 QUANTITY_CONTEXT, -1);
633 assembleg_1(streamstr_gc, AO1);
636 if (runtime_error_checking_switch)
637 { AO = veneer_routine(RT__ChPrintO_VR);
642 parse_expression(QUANTITY_CONTEXT),
643 QUANTITY_CONTEXT, -1);
644 INITAOT(&AO2, BYTECONSTANT_OT);
645 AO2.value = GOBJFIELD_NAME();
646 assembleg_3(aload_gc, AO1, AO2,
648 assembleg_1(streamstr_gc, stack_pointer);
651 AO = veneer_routine(DefArt_VR);
655 AO = veneer_routine(InDefArt_VR);
658 AO = veneer_routine(CDefArt_VR);
661 AO = veneer_routine(CInDefArt_VR);
664 AO = veneer_routine(PrintShortName_VR);
667 AO = veneer_routine(EnglishNumber_VR);
670 AO = veneer_routine(Print__Pname_VR);
673 error_named("A reserved word was used as a print specification:",
679 if (symbols[token_value].flags & UNKNOWN_SFLAG)
680 { INITAOT(&AO, CONSTANT_OT);
681 AO.value = token_value;
682 AO.marker = SYMBOL_MV;
683 AO.symindex = token_value;
686 { INITAOT(&AO, CONSTANT_OT);
687 AO.value = symbols[token_value].value;
688 AO.marker = IROUTINE_MV;
689 AO.symindex = token_value;
690 if (symbols[token_value].type != ROUTINE_T)
691 ebf_curtoken_error("printing routine name");
693 symbols[token_value].flags |= USED_SFLAG;
698 INITAOT(&AO2, ZEROCONSTANT_OT);
700 code_generate(parse_expression(QUANTITY_CONTEXT),
701 QUANTITY_CONTEXT, -1),
705 default: ebf_curtoken_error("print specification");
707 assembleg_1(streamnum_gc,
708 code_generate(parse_expression(QUANTITY_CONTEXT),
709 QUANTITY_CONTEXT, -1));
713 put_token_back(); put_token_back(); put_token_back();
714 misc_keywords.enabled = FALSE;
715 assembleg_1(streamnum_gc,
716 code_generate(parse_expression(QUANTITY_CONTEXT),
717 QUANTITY_CONTEXT, -1));
722 put_token_back(); misc_keywords.enabled = FALSE;
723 assembleg_1(streamnum_gc,
724 code_generate(parse_expression(QUANTITY_CONTEXT),
725 QUANTITY_CONTEXT, -1));
729 PrintTermDone: misc_keywords.enabled = FALSE;
733 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
734 if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
735 { ebf_curtoken_error("comma");
736 panic_mode_error_recovery(); return;
738 else get_next_token();
741 if (count == 0) ebf_curtoken_error("something to print");
744 INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
745 assembleg_1(streamchar_gc, AO);
746 INITAOTV(&AO, BYTECONSTANT_OT, 1);
747 assembleg_1(return_gc, AO);
751 /* Parse any number of ".Label;" lines before a statement.
752 Returns whether a statement can in fact follow. */
753 static int parse_named_label_statements()
755 while ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
756 { /* That is, a full stop, signifying a label */
759 if (token_type != SYMBOL_TT)
761 ebf_curtoken_error("label name");
765 if (symbols[token_value].flags & UNKNOWN_SFLAG)
766 { assign_symbol(token_value, next_label, LABEL_T);
767 symbols[token_value].flags |= USED_SFLAG;
768 assemble_label_no(next_label);
769 define_symbol_label(token_value);
773 { if (symbols[token_value].type != LABEL_T) {
774 ebf_curtoken_error("label name");
777 if (symbols[token_value].flags & CHANGE_SFLAG)
778 { symbols[token_value].flags &= (~(CHANGE_SFLAG));
779 assemble_label_no(symbols[token_value].value);
780 define_symbol_label(token_value);
782 else error_named("Duplicate definition of label:", token_text);
786 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
787 { ebf_curtoken_error("';'");
788 put_token_back(); return FALSE;
791 /* Interesting point of Inform grammar: a statement can only
792 consist solely of a label when it is immediately followed
796 if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
797 { put_token_back(); return FALSE;
799 /* The following line prevents labels from influencing the positions
800 of sequence points. */
801 statement_debug_location = get_token_location();
803 /* Another label might follow */
806 /* On with the statement */
810 static void parse_statement_z(int break_label, int continue_label)
811 { int ln, ln2, ln3, ln4, flag;
812 int pre_unreach, labelexists;
813 assembly_operand AO, AO2, AO3, AO4;
814 debug_location spare_debug_location1, spare_debug_location2;
818 if ((token_type == SEP_TT) && (token_value == HASH_SEP))
819 { parse_directive(TRUE);
820 parse_statement(break_label, continue_label); return;
823 if ((token_type == SEP_TT) && (token_value == AT_SEP))
824 { parse_assembly(); return;
827 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
829 if (token_type == DQ_TT)
830 { parse_print_z(TRUE); return;
833 if ((token_type == SEP_TT) && (token_value == LESS_SEP))
834 { parse_action(); goto StatementTerminator; }
836 if (token_type == EOF_TT)
837 { ebf_curtoken_error("statement"); return; }
839 /* If we don't see a keyword, this must be a function call or
840 other expression-with-side-effects. */
841 if (token_type != STATEMENT_TT)
843 AO = parse_expression(VOID_CONTEXT);
844 code_generate(AO, VOID_CONTEXT, -1);
845 if (vivc_flag) { panic_mode_error_recovery(); return; }
846 goto StatementTerminator;
849 statements.enabled = FALSE;
853 /* -------------------------------------------------------------------- */
854 /* box <string-1> ... <string-n> -------------------------------------- */
855 /* -------------------------------------------------------------------- */
858 if (version_number == 3)
859 warning("The 'box' statement has no effect in a version 3 game");
860 INITAOT(&AO3, LONG_CONSTANT_OT);
861 AO3.value = begin_table_array();
862 AO3.marker = ARRAY_MV;
866 if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
868 if (token_type != DQ_TT)
869 ebf_curtoken_error("text of box line in double-quotes");
871 for (i=0, j=0; token_text[i] != 0; j++)
872 if (token_text[i] == '@')
873 { if (token_text[i+1] == '@')
875 while (isdigit(token_text[i])) i++;
879 if (token_text[i] != 0) i++;
880 if (token_text[i] != 0) i++;
884 if (j > ln2) ln2 = j;
887 array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
889 finish_array(ln, FALSE);
891 error("No lines of text given for 'box' display");
893 if (version_number == 3) return;
895 INITAOTV(&AO2, SHORT_CONSTANT_OT, ln2);
896 INITAOTV(&AO4, VARIABLE_OT, 255);
897 assemblez_3_to(call_vs_zc, veneer_routine(Box__Routine_VR),
901 /* -------------------------------------------------------------------- */
902 /* break -------------------------------------------------------------- */
903 /* -------------------------------------------------------------------- */
906 if (break_label == -1)
907 error("'break' can only be used in a loop or 'switch' block");
909 assemblez_jump(break_label);
912 /* -------------------------------------------------------------------- */
913 /* continue ----------------------------------------------------------- */
914 /* -------------------------------------------------------------------- */
917 if (continue_label == -1)
918 error("'continue' can only be used in a loop block");
920 assemblez_jump(continue_label);
923 /* -------------------------------------------------------------------- */
924 /* do <codeblock> until (<condition>) --------------------------------- */
925 /* -------------------------------------------------------------------- */
928 assemble_label_no(ln = next_label++);
929 ln2 = next_label++; ln3 = next_label++;
930 parse_code_block(ln3, ln2, 0);
931 statements.enabled = TRUE;
933 if ((token_type == STATEMENT_TT)
934 && (token_value == UNTIL_CODE))
935 { assemble_forward_label_no(ln2);
936 match_open_bracket();
937 AO = parse_expression(CONDITION_CONTEXT);
938 match_close_bracket();
939 code_generate(AO, CONDITION_CONTEXT, ln);
941 else error("'do' without matching 'until'");
943 assemble_forward_label_no(ln3);
946 /* -------------------------------------------------------------------- */
947 /* font on/off -------------------------------------------------------- */
948 /* -------------------------------------------------------------------- */
951 misc_keywords.enabled = TRUE;
953 misc_keywords.enabled = FALSE;
954 if ((token_type != MISC_KEYWORD_TT)
955 || ((token_value != ON_MK)
956 && (token_value != OFF_MK)))
957 { ebf_curtoken_error("'on' or 'off'");
958 panic_mode_error_recovery();
962 if (version_number >= 5)
963 { /* Use the V5 @set_font opcode, setting font 4
964 (for font off) or 1 (for font on). */
965 INITAOT(&AO, SHORT_CONSTANT_OT);
966 if (token_value == ON_MK)
970 assemblez_1_to(set_font_zc, AO, temp_var1);
974 /* Set the fixed-pitch header bit. */
975 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
976 INITAOTV(&AO2, SHORT_CONSTANT_OT, 8);
977 INITAOTV(&AO3, VARIABLE_OT, 255);
978 assemblez_2_to(loadw_zc, AO, AO2, AO3);
980 if (token_value == ON_MK)
981 { INITAOTV(&AO4, LONG_CONSTANT_OT, 0xfffd);
982 assemblez_2_to(and_zc, AO4, AO3, AO3);
985 { INITAOTV(&AO4, SHORT_CONSTANT_OT, 2);
986 assemblez_2_to(or_zc, AO4, AO3, AO3);
989 assemblez_3(storew_zc, AO, AO2, AO3);
992 /* -------------------------------------------------------------------- */
993 /* for (<initialisation> : <continue-condition> : <updating>) --------- */
994 /* -------------------------------------------------------------------- */
996 /* Note that it's legal for any or all of the three sections of a
997 'for' specification to be empty. This 'for' implementation
998 often wastes 3 bytes with a redundant branch rather than keep
999 expression parse trees for long periods (as previous versions
1000 of Inform did, somewhat crudely by simply storing the textual
1001 form of a 'for' loop). It is adequate for now. */
1004 match_open_bracket();
1007 /* Initialisation code */
1008 AO.type = OMITTED_OT;
1009 spare_debug_location1 = statement_debug_location;
1010 AO2.type = OMITTED_OT; flag = 0;
1011 spare_debug_location2 = statement_debug_location;
1013 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1015 if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
1016 { sequence_point_follows = TRUE;
1017 statement_debug_location = get_token_location();
1018 code_generate(parse_expression(FORINIT_CONTEXT),
1022 if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
1024 if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
1025 { assemble_label_no(ln = next_label++);
1027 parse_code_block(ln2, ln, 0);
1028 sequence_point_follows = FALSE;
1029 if (!execution_never_reaches_here)
1031 assemble_forward_label_no(ln2);
1037 if (!match_colon()) break;
1041 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1043 spare_debug_location1 = get_token_location();
1044 AO = parse_expression(CONDITION_CONTEXT);
1045 if (!match_colon()) break;
1050 if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
1052 spare_debug_location2 = get_token_location();
1053 AO2 = parse_expression(VOID_CONTEXT);
1054 match_close_bracket();
1055 flag = test_for_incdec(AO2);
1062 if ((AO2.type == OMITTED_OT) || (flag != 0))
1064 assemble_label_no(ln);
1065 if (flag==0) assemble_label_no(ln2);
1067 /* The "finished yet?" condition */
1069 if (AO.type != OMITTED_OT)
1070 { sequence_point_follows = TRUE;
1071 statement_debug_location = spare_debug_location1;
1072 code_generate(AO, CONDITION_CONTEXT, ln3);
1078 /* This is the jump which could be avoided with the aid
1079 of long-term expression storage */
1081 sequence_point_follows = FALSE;
1082 assemblez_jump(ln2);
1084 /* The "update" part */
1086 assemble_label_no(ln);
1087 sequence_point_follows = TRUE;
1088 statement_debug_location = spare_debug_location2;
1089 code_generate(AO2, VOID_CONTEXT, -1);
1091 assemble_label_no(ln2);
1093 /* The "finished yet?" condition */
1095 if (AO.type != OMITTED_OT)
1096 { sequence_point_follows = TRUE;
1097 statement_debug_location = spare_debug_location1;
1098 code_generate(AO, CONDITION_CONTEXT, ln3);
1104 /* In this optimised case, update code is at the end
1105 of the loop block, so "continue" goes there */
1107 parse_code_block(ln3, ln2, 0);
1108 assemble_label_no(ln2);
1110 sequence_point_follows = TRUE;
1111 statement_debug_location = spare_debug_location2;
1113 { INITAOTV(&AO3, SHORT_CONSTANT_OT, flag);
1114 assemblez_1(inc_zc, AO3);
1117 { INITAOTV(&AO3, SHORT_CONSTANT_OT, -flag);
1118 assemblez_1(dec_zc, AO3);
1124 /* In the unoptimised case, update code is at the
1125 start of the loop block, so "continue" goes there */
1127 parse_code_block(ln3, ln, 0);
1128 if (!execution_never_reaches_here)
1129 { sequence_point_follows = FALSE;
1134 assemble_forward_label_no(ln3);
1137 /* -------------------------------------------------------------------- */
1138 /* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
1139 /* -------------------------------------------------------------------- */
1142 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1143 QUANTITY_CONTEXT, -1);
1144 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
1145 if ((AO.type == VARIABLE_OT) && (AO.value == 0))
1146 { INITAOTV(&AO, SHORT_CONSTANT_OT, 252);
1147 if (version_number != 6) assemblez_1(pull_zc, AO);
1148 else assemblez_0_to(pull_zc, AO);
1149 AO.type = VARIABLE_OT;
1154 if ((token_type == SEP_TT)&&(token_value == SEMICOLON_SEP))
1156 if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
1162 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1163 QUANTITY_CONTEXT, -1);
1164 check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
1165 if (runtime_error_checking_switch)
1166 { ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
1167 if (version_number >= 5)
1168 assemblez_3(call_vn_zc, veneer_routine(ln2),
1172 assemblez_3_to(call_zc, veneer_routine(ln2),
1173 AO, AO2, temp_var1);
1177 assemblez_2(ln, AO, AO2);
1180 /* -------------------------------------------------------------------- */
1181 /* if (<condition>) <codeblock> [else <codeblock>] -------------------- */
1182 /* -------------------------------------------------------------------- */
1185 flag = FALSE; /* set if there's an "else" */
1187 pre_unreach = execution_never_reaches_here;
1189 match_open_bracket();
1190 AO = parse_expression(CONDITION_CONTEXT);
1191 match_close_bracket();
1193 statements.enabled = TRUE;
1195 if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
1198 if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
1206 code_generate(AO, CONDITION_CONTEXT, ln);
1208 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
1209 /* If the condition never falls through to here, then
1210 it was an "if (0)" test. Our convention is to skip
1211 the "not reached" warnings for this case. */
1212 execution_never_reaches_here |= EXECSTATE_NOWARN;
1215 /* The "if" block */
1216 if (ln >= 0) parse_code_block(break_label, continue_label, 0);
1219 if ((token_type != SEP_TT)
1220 || (token_value != SEMICOLON_SEP))
1221 { ebf_curtoken_error("';'");
1226 statements.enabled = TRUE;
1229 /* An #if directive around the ELSE clause is legal. */
1230 while ((token_type == SEP_TT) && (token_value == HASH_SEP))
1231 { parse_directive(TRUE);
1232 statements.enabled = TRUE;
1236 if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
1239 { ln2 = next_label++;
1240 if (!execution_never_reaches_here)
1241 { sequence_point_follows = FALSE;
1242 assemblez_jump(ln2);
1246 else put_token_back();
1248 /* The "else" label (or end of statement, if there is no "else") */
1249 labelexists = FALSE;
1250 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
1254 /* If labelexists is false, then we started with
1255 "if (1)". In this case, we don't want a "not
1256 reached" warning on the "else" block. We
1257 temporarily disable the NOWARN flag, and restore it
1259 int saved_unreach = 0;
1260 if (execution_never_reaches_here && !labelexists) {
1261 saved_unreach = execution_never_reaches_here;
1262 execution_never_reaches_here |= EXECSTATE_NOWARN;
1265 /* The "else" block */
1266 parse_code_block(break_label, continue_label, 0);
1268 if (execution_never_reaches_here && !labelexists) {
1269 if (saved_unreach & EXECSTATE_NOWARN)
1270 execution_never_reaches_here |= EXECSTATE_NOWARN;
1272 execution_never_reaches_here &= ~EXECSTATE_NOWARN;
1275 /* The post-"else" label */
1276 if (ln >= 0) assemble_forward_label_no(ln2);
1280 /* There was no "else". If we're unreachable, then the
1281 statement returned unconditionally, which means
1282 "if (1) return". Skip warnings. */
1283 if (!pre_unreach && execution_never_reaches_here) {
1284 execution_never_reaches_here |= EXECSTATE_NOWARN;
1290 /* -------------------------------------------------------------------- */
1291 /* inversion ---------------------------------------------------------- */
1292 /* -------------------------------------------------------------------- */
1294 case INVERSION_CODE:
1295 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
1296 INITAOT(&AO2, SHORT_CONSTANT_OT);
1299 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1300 assemblez_1(print_char_zc, temp_var1);
1302 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1303 assemblez_1(print_char_zc, temp_var1);
1305 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1306 assemblez_1(print_char_zc, temp_var1);
1308 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1309 assemblez_1(print_char_zc, temp_var1);
1312 /* -------------------------------------------------------------------- */
1313 /* jump <label> ------------------------------------------------------- */
1314 /* -------------------------------------------------------------------- */
1317 assemblez_jump(parse_label());
1320 /* -------------------------------------------------------------------- */
1321 /* move <expression> to <expression> ---------------------------------- */
1322 /* -------------------------------------------------------------------- */
1325 misc_keywords.enabled = TRUE;
1326 AO = parse_expression(QUANTITY_CONTEXT);
1329 misc_keywords.enabled = FALSE;
1330 if ((token_type != MISC_KEYWORD_TT)
1331 || (token_value != TO_MK))
1332 { ebf_curtoken_error("'to'");
1333 panic_mode_error_recovery();
1337 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1338 QUANTITY_CONTEXT, -1);
1339 AO = code_generate(AO, QUANTITY_CONTEXT, -1);
1340 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
1341 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
1342 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
1343 { if (version_number >= 5)
1344 assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
1347 { assemblez_3_to(call_zc, veneer_routine(RT__ChT_VR),
1348 AO, AO2, temp_var1);
1352 assemblez_2(insert_obj_zc, AO, AO2);
1355 /* -------------------------------------------------------------------- */
1356 /* new_line ----------------------------------------------------------- */
1357 /* -------------------------------------------------------------------- */
1359 case NEW_LINE_CODE: assemblez_0(new_line_zc); break;
1361 /* -------------------------------------------------------------------- */
1362 /* objectloop (<initialisation>) <codeblock> -------------------------- */
1363 /* -------------------------------------------------------------------- */
1365 case OBJECTLOOP_CODE:
1367 match_open_bracket();
1369 INITAOT(&AO, VARIABLE_OT);
1370 if (token_type == LOCAL_VARIABLE_TT)
1371 AO.value = token_value;
1373 if ((token_type == SYMBOL_TT) &&
1374 (symbols[token_value].type == GLOBAL_VARIABLE_T))
1375 AO.value = symbols[token_value].value;
1377 { ebf_curtoken_error("'objectloop' variable");
1378 panic_mode_error_recovery(); break;
1380 misc_keywords.enabled = TRUE;
1381 get_next_token(); flag = TRUE;
1382 misc_keywords.enabled = FALSE;
1383 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
1387 if ((token_type == MISC_KEYWORD_TT)
1388 && (token_value == NEAR_MK)) ln = 1;
1389 if ((token_type == MISC_KEYWORD_TT)
1390 && (token_value == FROM_MK)) ln = 2;
1391 if ((token_type == CND_TT) && (token_value == IN_COND))
1394 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
1401 { /* Old style (Inform 5) objectloops: note that we
1402 implement objectloop (a in b) in the old way since
1403 this runs through objects in a different order from
1404 the new way, and there may be existing Inform code
1406 assembly_operand AO4;
1409 sequence_point_follows = TRUE;
1410 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1411 QUANTITY_CONTEXT, -1);
1412 match_close_bracket();
1414 { INITAOTV(&AO3, VARIABLE_OT, 0);
1415 if (runtime_error_checking_switch)
1416 AO2 = check_nonzero_at_runtime(AO2, -1,
1418 assemblez_1_to(get_parent_zc, AO2, AO3);
1419 assemblez_objcode(get_child_zc, AO3, AO3, -2, TRUE);
1423 { INITAOTV(&AO3, VARIABLE_OT, 0);
1424 if (runtime_error_checking_switch)
1426 AO2 = check_nonzero_at_runtime(AO2, -1,
1429 assemblez_objcode(get_child_zc, AO2, AO3, -2, TRUE);
1432 assemblez_store(AO, AO2);
1433 assemblez_1_branch(jz_zc, AO, ln2 = next_label++, TRUE);
1434 assemble_label_no(ln4 = next_label++);
1435 parse_code_block(ln2, ln3 = next_label++, 0);
1436 sequence_point_follows = FALSE;
1437 assemble_label_no(ln3);
1438 if (runtime_error_checking_switch)
1439 { AO2 = check_nonzero_at_runtime(AO, ln2,
1442 && ((AO4.type != VARIABLE_OT)||(AO4.value != 0))
1443 && ((AO4.type != VARIABLE_OT)
1444 ||(AO4.value != AO.value)))
1445 { assembly_operand en_ao;
1446 INITAOTV(&en_ao, SHORT_CONSTANT_OT, OBJECTLOOP_BROKEN_RTE);
1447 assemblez_2_branch(jin_zc, AO, AO4,
1449 assemblez_3(call_vn_zc, veneer_routine(RT__Err_VR),
1451 assemblez_jump(ln2);
1452 assemble_label_no(next_label++);
1456 assemblez_objcode(get_sibling_zc, AO2, AO, ln4, TRUE);
1457 assemble_label_no(ln2);
1461 sequence_point_follows = TRUE;
1462 INITAOTV(&AO2, SHORT_CONSTANT_OT, 1);
1463 assemblez_store(AO, AO2);
1465 assemble_label_no(ln = next_label++);
1471 sequence_point_follows = TRUE;
1472 code_generate(parse_expression(CONDITION_CONTEXT),
1473 CONDITION_CONTEXT, ln3);
1474 match_close_bracket();
1476 parse_code_block(ln2, ln3, 0);
1478 sequence_point_follows = FALSE;
1479 assemble_label_no(ln3);
1481 INITAOTV(&AO2, LONG_CONSTANT_OT, no_objects);
1482 AO2.marker = NO_OBJS_MV;
1483 assemblez_2_branch(jg_zc, AO, AO2, ln2, TRUE);
1485 assemble_label_no(ln2);
1488 /* -------------------------------------------------------------------- */
1489 /* (see routine above) ------------------------------------------------ */
1490 /* -------------------------------------------------------------------- */
1494 parse_print_z(FALSE); return;
1495 case PRINT_RET_CODE:
1497 parse_print_z(TRUE); return;
1499 /* -------------------------------------------------------------------- */
1500 /* quit --------------------------------------------------------------- */
1501 /* -------------------------------------------------------------------- */
1503 case QUIT_CODE: assemblez_0(quit_zc); break;
1505 /* -------------------------------------------------------------------- */
1506 /* read <expression> <expression> [<Routine>] ------------------------- */
1507 /* -------------------------------------------------------------------- */
1510 INITAOTV(&AO, VARIABLE_OT, 252);
1512 code_generate(parse_expression(QUANTITY_CONTEXT),
1513 QUANTITY_CONTEXT, -1));
1514 if (version_number > 3)
1515 { INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
1516 INITAOTV(&AO4, SHORT_CONSTANT_OT, 0);
1517 assemblez_3(storeb_zc, AO, AO3, AO4);
1519 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1520 QUANTITY_CONTEXT, -1);
1523 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
1526 { if (version_number == 3)
1528 "In Version 3 no status-line drawing routine can be given");
1530 { assembly_operand AO5;
1531 /* Move the temp4 (buffer) value to the stack,
1532 since the routine might alter temp4. */
1533 assemblez_store(stack_pointer, AO);
1536 AO5 = parse_expression(CONSTANT_CONTEXT);
1538 if (version_number >= 5)
1539 assemblez_1(call_1n_zc, AO5);
1541 assemblez_1_to(call_zc, AO5, temp_var1);
1545 if (version_number > 4)
1546 { assemblez_2_to(aread_zc, AO, AO2, temp_var1);
1548 else assemblez_2(sread_zc, AO, AO2);
1551 /* -------------------------------------------------------------------- */
1552 /* remove <expression> ------------------------------------------------ */
1553 /* -------------------------------------------------------------------- */
1556 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1557 QUANTITY_CONTEXT, -1);
1558 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
1559 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
1560 { if (version_number >= 5)
1561 assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR),
1564 { assemblez_2_to(call_zc, veneer_routine(RT__ChR_VR),
1569 assemblez_1(remove_obj_zc, AO);
1572 /* -------------------------------------------------------------------- */
1573 /* restore <label> ---------------------------------------------------- */
1574 /* -------------------------------------------------------------------- */
1577 if (version_number < 5)
1578 assemblez_0_branch(restore_zc, parse_label(), TRUE);
1580 { INITAOTV(&AO2, SHORT_CONSTANT_OT, 2);
1581 assemblez_0_to(restore_zc, temp_var1);
1582 assemblez_2_branch(je_zc, temp_var1, AO2, parse_label(), TRUE);
1586 /* -------------------------------------------------------------------- */
1587 /* return [<expression>] ---------------------------------------------- */
1588 /* -------------------------------------------------------------------- */
1592 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
1593 { assemblez_0(rtrue_zc); return; }
1595 AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
1596 QUANTITY_CONTEXT, -1);
1597 if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 0)
1598 && (AO.marker == 0))
1599 { assemblez_0(rfalse_zc); break; }
1600 if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 1)
1601 && (AO.marker == 0))
1602 { assemblez_0(rtrue_zc); break; }
1603 if ((AO.type == VARIABLE_OT) && (AO.value == 0))
1604 { assemblez_0(ret_popped_zc); break; }
1605 assemblez_1(ret_zc, AO);
1608 /* -------------------------------------------------------------------- */
1609 /* rfalse ------------------------------------------------------------- */
1610 /* -------------------------------------------------------------------- */
1612 case RFALSE_CODE: assemblez_0(rfalse_zc); break;
1614 /* -------------------------------------------------------------------- */
1615 /* rtrue -------------------------------------------------------------- */
1616 /* -------------------------------------------------------------------- */
1618 case RTRUE_CODE: assemblez_0(rtrue_zc); break;
1620 /* -------------------------------------------------------------------- */
1621 /* save <label> ------------------------------------------------------- */
1622 /* -------------------------------------------------------------------- */
1625 if (version_number < 5)
1626 assemblez_0_branch(save_zc, parse_label(), TRUE);
1628 { INITAOTV(&AO, VARIABLE_OT, 255);
1629 assemblez_0_to(save_zc, AO);
1630 assemblez_1_branch(jz_zc, AO, parse_label(), FALSE);
1634 /* -------------------------------------------------------------------- */
1635 /* spaces <expression> ------------------------------------------------ */
1636 /* -------------------------------------------------------------------- */
1639 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1640 QUANTITY_CONTEXT, -1);
1641 INITAOTV(&AO2, VARIABLE_OT, 255);
1643 assemblez_store(AO2, AO);
1645 INITAOTV(&AO, SHORT_CONSTANT_OT, 32);
1646 INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
1648 assemblez_2_branch(jl_zc, AO2, AO3, ln = next_label++, TRUE);
1649 assemble_label_no(ln2 = next_label++);
1650 assemblez_1(print_char_zc, AO);
1652 assemblez_1_branch(jz_zc, AO2, ln2, FALSE);
1653 assemble_label_no(ln);
1656 /* -------------------------------------------------------------------- */
1657 /* string <expression> <literal-string> ------------------------------- */
1658 /* -------------------------------------------------------------------- */
1661 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
1662 INITAOTV(&AO2, SHORT_CONSTANT_OT, 12);
1663 INITAOTV(&AO3, VARIABLE_OT, 252);
1664 assemblez_2_to(loadw_zc, AO, AO2, AO3);
1665 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1666 QUANTITY_CONTEXT, -1);
1667 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
1668 /* Compile-time check */
1669 if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
1670 error_max_dynamic_strings(AO2.value);
1675 if (token_type == DQ_TT)
1676 { INITAOT(&AO4, LONG_CONSTANT_OT);
1677 /* This string must be in low memory so that the
1678 dynamic string table can refer to it. */
1679 AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
1683 AO4 = parse_expression(CONSTANT_CONTEXT);
1685 assemblez_3(storew_zc, AO3, AO2, AO4);
1688 /* -------------------------------------------------------------------- */
1689 /* style roman/reverse/bold/underline/fixed --------------------------- */
1690 /* -------------------------------------------------------------------- */
1693 if (version_number==3)
1695 "The 'style' statement cannot be used for Version 3 games");
1696 panic_mode_error_recovery();
1700 misc_keywords.enabled = TRUE;
1702 misc_keywords.enabled = FALSE;
1703 if ((token_type != MISC_KEYWORD_TT)
1704 || ((token_value != ROMAN_MK)
1705 && (token_value != REVERSE_MK)
1706 && (token_value != BOLD_MK)
1707 && (token_value != UNDERLINE_MK)
1708 && (token_value != FIXED_MK)))
1709 { ebf_curtoken_error(
1710 "'roman', 'bold', 'underline', 'reverse' or 'fixed'");
1711 panic_mode_error_recovery();
1715 INITAOT(&AO, SHORT_CONSTANT_OT);
1717 { case ROMAN_MK: AO.value = 0; break;
1718 case REVERSE_MK: AO.value = 1; break;
1719 case BOLD_MK: AO.value = 2; break;
1720 case UNDERLINE_MK: AO.value = 4; break;
1721 case FIXED_MK: AO.value = 8; break;
1723 assemblez_1(set_text_style_zc, AO); break;
1725 /* -------------------------------------------------------------------- */
1726 /* switch (<expression>) <codeblock> ---------------------------------- */
1727 /* -------------------------------------------------------------------- */
1730 match_open_bracket();
1731 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1732 QUANTITY_CONTEXT, -1);
1733 match_close_bracket();
1735 INITAOTV(&AO2, VARIABLE_OT, 255);
1736 assemblez_store(AO2, AO);
1738 parse_code_block(ln = next_label++, continue_label, 1);
1739 assemble_forward_label_no(ln);
1742 /* -------------------------------------------------------------------- */
1743 /* while (<condition>) <codeblock> ------------------------------------ */
1744 /* -------------------------------------------------------------------- */
1747 assemble_label_no(ln = next_label++);
1748 match_open_bracket();
1750 code_generate(parse_expression(CONDITION_CONTEXT),
1751 CONDITION_CONTEXT, ln2 = next_label++);
1752 match_close_bracket();
1754 parse_code_block(ln2, ln, 0);
1755 sequence_point_follows = FALSE;
1757 assemble_forward_label_no(ln2);
1760 /* -------------------------------------------------------------------- */
1763 error("'default' without matching 'switch'"); break;
1765 error("'else' without matching 'if'"); break;
1767 error("'until' without matching 'do'");
1768 panic_mode_error_recovery(); return;
1771 StatementTerminator:
1774 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
1775 { ebf_curtoken_error("';'");
1780 static void parse_statement_g(int break_label, int continue_label)
1781 { int ln, ln2, ln3, ln4, flag, onstack;
1782 int pre_unreach, labelexists;
1783 assembly_operand AO, AO2, AO3, AO4;
1784 debug_location spare_debug_location1, spare_debug_location2;
1788 if ((token_type == SEP_TT) && (token_value == HASH_SEP))
1789 { parse_directive(TRUE);
1790 parse_statement(break_label, continue_label); return;
1793 if ((token_type == SEP_TT) && (token_value == AT_SEP))
1794 { parse_assembly(); return;
1797 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
1799 if (token_type == DQ_TT)
1800 { parse_print_g(TRUE); return;
1803 if ((token_type == SEP_TT) && (token_value == LESS_SEP))
1804 { parse_action(); goto StatementTerminator; }
1806 if (token_type == EOF_TT)
1807 { ebf_curtoken_error("statement"); return; }
1809 /* If we don't see a keyword, this must be a function call or
1810 other expression-with-side-effects. */
1811 if (token_type != STATEMENT_TT)
1813 AO = parse_expression(VOID_CONTEXT);
1814 code_generate(AO, VOID_CONTEXT, -1);
1815 if (vivc_flag) { panic_mode_error_recovery(); return; }
1816 goto StatementTerminator;
1819 statements.enabled = FALSE;
1824 /* -------------------------------------------------------------------- */
1825 /* box <string-1> ... <string-n> -------------------------------------- */
1826 /* -------------------------------------------------------------------- */
1829 INITAOT(&AO3, CONSTANT_OT);
1830 AO3.value = begin_table_array();
1831 AO3.marker = ARRAY_MV;
1835 if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
1837 if (token_type != DQ_TT)
1838 ebf_curtoken_error("text of box line in double-quotes");
1840 for (i=0, j=0; token_text[i] != 0; j++)
1841 if (token_text[i] == '@')
1842 { if (token_text[i+1] == '@')
1844 while (isdigit(token_text[i])) i++;
1848 if (token_text[i] != 0) i++;
1849 if (token_text[i] != 0) i++;
1853 if (j > ln2) ln2 = j;
1856 array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
1858 finish_array(ln, FALSE);
1860 error("No lines of text given for 'box' display");
1863 AO2.value = ln2; set_constant_ot(&AO2);
1864 assembleg_call_2(veneer_routine(Box__Routine_VR),
1865 AO2, AO3, zero_operand);
1868 /* -------------------------------------------------------------------- */
1869 /* break -------------------------------------------------------------- */
1870 /* -------------------------------------------------------------------- */
1873 if (break_label == -1)
1874 error("'break' can only be used in a loop or 'switch' block");
1876 assembleg_jump(break_label);
1879 /* -------------------------------------------------------------------- */
1880 /* continue ----------------------------------------------------------- */
1881 /* -------------------------------------------------------------------- */
1884 if (continue_label == -1)
1885 error("'continue' can only be used in a loop block");
1887 assembleg_jump(continue_label);
1890 /* -------------------------------------------------------------------- */
1891 /* do <codeblock> until (<condition>) --------------------------------- */
1892 /* -------------------------------------------------------------------- */
1895 assemble_label_no(ln = next_label++);
1896 ln2 = next_label++; ln3 = next_label++;
1897 parse_code_block(ln3, ln2, 0);
1898 statements.enabled = TRUE;
1900 if ((token_type == STATEMENT_TT)
1901 && (token_value == UNTIL_CODE))
1902 { assemble_forward_label_no(ln2);
1903 match_open_bracket();
1904 AO = parse_expression(CONDITION_CONTEXT);
1905 match_close_bracket();
1906 code_generate(AO, CONDITION_CONTEXT, ln);
1908 else error("'do' without matching 'until'");
1910 assemble_forward_label_no(ln3);
1913 /* -------------------------------------------------------------------- */
1914 /* font on/off -------------------------------------------------------- */
1915 /* -------------------------------------------------------------------- */
1918 misc_keywords.enabled = TRUE;
1920 misc_keywords.enabled = FALSE;
1921 if ((token_type != MISC_KEYWORD_TT)
1922 || ((token_value != ON_MK)
1923 && (token_value != OFF_MK)))
1924 { ebf_curtoken_error("'on' or 'off'");
1925 panic_mode_error_recovery();
1929 /* Call glk_set_style(normal or preformatted) */
1932 set_constant_ot(&AO);
1933 if (token_value == ON_MK)
1937 assembleg_call_2(veneer_routine(Glk__Wrap_VR),
1938 AO, AO2, zero_operand);
1941 /* -------------------------------------------------------------------- */
1942 /* for (<initialisation> : <continue-condition> : <updating>) --------- */
1943 /* -------------------------------------------------------------------- */
1945 /* Note that it's legal for any or all of the three sections of a
1946 'for' specification to be empty. This 'for' implementation
1947 often wastes 3 bytes with a redundant branch rather than keep
1948 expression parse trees for long periods (as previous versions
1949 of Inform did, somewhat crudely by simply storing the textual
1950 form of a 'for' loop). It is adequate for now. */
1953 match_open_bracket();
1956 /* Initialisation code */
1957 AO.type = OMITTED_OT;
1958 spare_debug_location1 = statement_debug_location;
1959 AO2.type = OMITTED_OT; flag = 0;
1960 spare_debug_location2 = statement_debug_location;
1962 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1964 if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
1965 { sequence_point_follows = TRUE;
1966 statement_debug_location = get_token_location();
1967 code_generate(parse_expression(FORINIT_CONTEXT),
1971 if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
1973 if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
1974 { assemble_label_no(ln = next_label++);
1976 parse_code_block(ln2, ln, 0);
1977 sequence_point_follows = FALSE;
1978 if (!execution_never_reaches_here)
1980 assemble_forward_label_no(ln2);
1986 if (!match_colon()) break;
1990 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1992 spare_debug_location1 = get_token_location();
1993 AO = parse_expression(CONDITION_CONTEXT);
1994 if (!match_colon()) break;
1999 if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
2001 spare_debug_location2 = get_token_location();
2002 AO2 = parse_expression(VOID_CONTEXT);
2003 match_close_bracket();
2004 flag = test_for_incdec(AO2);
2011 if ((AO2.type == OMITTED_OT) || (flag != 0))
2013 assemble_label_no(ln);
2014 if (flag==0) assemble_label_no(ln2);
2016 /* The "finished yet?" condition */
2018 if (AO.type != OMITTED_OT)
2019 { sequence_point_follows = TRUE;
2020 statement_debug_location = spare_debug_location1;
2021 code_generate(AO, CONDITION_CONTEXT, ln3);
2027 /* This is the jump which could be avoided with the aid
2028 of long-term expression storage */
2030 sequence_point_follows = FALSE;
2031 assembleg_jump(ln2);
2033 /* The "update" part */
2035 assemble_label_no(ln);
2036 sequence_point_follows = TRUE;
2037 statement_debug_location = spare_debug_location2;
2038 code_generate(AO2, VOID_CONTEXT, -1);
2040 assemble_label_no(ln2);
2042 /* The "finished yet?" condition */
2044 if (AO.type != OMITTED_OT)
2045 { sequence_point_follows = TRUE;
2046 statement_debug_location = spare_debug_location1;
2047 code_generate(AO, CONDITION_CONTEXT, ln3);
2053 /* In this optimised case, update code is at the end
2054 of the loop block, so "continue" goes there */
2056 parse_code_block(ln3, ln2, 0);
2057 assemble_label_no(ln2);
2059 sequence_point_follows = TRUE;
2060 statement_debug_location = spare_debug_location2;
2064 if (AO3.value >= MAX_LOCAL_VARIABLES)
2065 AO3.type = GLOBALVAR_OT;
2067 AO3.type = LOCALVAR_OT;
2068 assembleg_3(add_gc, AO3, one_operand, AO3);
2073 if (AO3.value >= MAX_LOCAL_VARIABLES)
2074 AO3.type = GLOBALVAR_OT;
2076 AO3.type = LOCALVAR_OT;
2077 assembleg_3(sub_gc, AO3, one_operand, AO3);
2083 /* In the unoptimised case, update code is at the
2084 start of the loop block, so "continue" goes there */
2086 parse_code_block(ln3, ln, 0);
2087 if (!execution_never_reaches_here)
2088 { sequence_point_follows = FALSE;
2093 assemble_forward_label_no(ln3);
2096 /* -------------------------------------------------------------------- */
2097 /* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
2098 /* -------------------------------------------------------------------- */
2101 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2102 QUANTITY_CONTEXT, -1);
2103 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
2104 if ((AO.type == LOCALVAR_OT) && (AO.value == 0))
2111 if ((token_type == SEP_TT)
2112 && (token_value == SEMICOLON_SEP)) {
2114 assembleg_2(copy_gc, stack_pointer, zero_operand);
2118 if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
2124 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2125 QUANTITY_CONTEXT, -1);
2126 check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
2127 if (runtime_error_checking_switch && (!veneer_mode))
2128 { ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR);
2129 if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
2130 /* already on stack */
2133 assembleg_store(stack_pointer, AO2);
2136 assembleg_2(stkpeek_gc, one_operand, stack_pointer);
2138 assembleg_store(stack_pointer, AO);
2139 assembleg_3(call_gc, veneer_routine(ln2), two_operand,
2143 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
2145 set_constant_ot(&AO2);
2148 INITAOTV(&AO3, BYTECONSTANT_OT, 8);
2149 assembleg_3(add_gc, AO2, AO3, stack_pointer);
2150 AO2 = stack_pointer;
2153 if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0))
2154 assembleg_2(stkpeek_gc, one_operand,
2157 assembleg_2(stkpeek_gc, zero_operand,
2164 assembleg_3(astorebit_gc, AO, AO2, AO3);
2168 /* -------------------------------------------------------------------- */
2169 /* if (<condition>) <codeblock> [else <codeblock>] -------------------- */
2170 /* -------------------------------------------------------------------- */
2173 flag = FALSE; /* set if there's an "else" */
2175 pre_unreach = execution_never_reaches_here;
2177 match_open_bracket();
2178 AO = parse_expression(CONDITION_CONTEXT);
2179 match_close_bracket();
2181 statements.enabled = TRUE;
2183 if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
2186 if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
2194 code_generate(AO, CONDITION_CONTEXT, ln);
2196 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
2197 /* If the condition never falls through to here, then
2198 it was an "if (0)" test. Our convention is to skip
2199 the "not reached" warnings for this case. */
2200 execution_never_reaches_here |= EXECSTATE_NOWARN;
2203 /* The "if" block */
2204 if (ln >= 0) parse_code_block(break_label, continue_label, 0);
2207 if ((token_type != SEP_TT)
2208 || (token_value != SEMICOLON_SEP))
2209 { ebf_curtoken_error("';'");
2214 statements.enabled = TRUE;
2217 /* An #if directive around the ELSE clause is legal. */
2218 while ((token_type == SEP_TT) && (token_value == HASH_SEP))
2219 { parse_directive(TRUE);
2220 statements.enabled = TRUE;
2224 if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
2227 { ln2 = next_label++;
2228 if (!execution_never_reaches_here)
2229 { sequence_point_follows = FALSE;
2230 assembleg_jump(ln2);
2234 else put_token_back();
2236 /* The "else" label (or end of statement, if there is no "else") */
2237 labelexists = FALSE;
2238 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
2242 /* If labelexists is false, then we started with
2243 "if (1)". In this case, we don't want a "not
2244 reached" warning on the "else" block. We
2245 temporarily disable the NOWARN flag, and restore it
2247 int saved_unreach = 0;
2248 if (execution_never_reaches_here && !labelexists) {
2249 saved_unreach = execution_never_reaches_here;
2250 execution_never_reaches_here |= EXECSTATE_NOWARN;
2253 /* The "else" block */
2254 parse_code_block(break_label, continue_label, 0);
2256 if (execution_never_reaches_here && !labelexists) {
2257 if (saved_unreach & EXECSTATE_NOWARN)
2258 execution_never_reaches_here |= EXECSTATE_NOWARN;
2260 execution_never_reaches_here &= ~EXECSTATE_NOWARN;
2263 /* The post-"else" label */
2264 if (ln >= 0) assemble_forward_label_no(ln2);
2268 /* There was no "else". If we're unreachable, then the
2269 statement returned unconditionally, which means
2270 "if (1) return". Skip warnings. */
2271 if (!pre_unreach && execution_never_reaches_here) {
2272 execution_never_reaches_here |= EXECSTATE_NOWARN;
2278 /* -------------------------------------------------------------------- */
2279 /* inversion ---------------------------------------------------------- */
2280 /* -------------------------------------------------------------------- */
2282 case INVERSION_CODE:
2283 INITAOTV(&AO2, DEREFERENCE_OT, GLULX_HEADER_SIZE+8);
2284 assembleg_2(copyb_gc, AO2, stack_pointer);
2285 assembleg_1(streamchar_gc, stack_pointer);
2286 AO2.value = GLULX_HEADER_SIZE+9;
2287 assembleg_2(copyb_gc, AO2, stack_pointer);
2288 assembleg_1(streamchar_gc, stack_pointer);
2289 AO2.value = GLULX_HEADER_SIZE+10;
2290 assembleg_2(copyb_gc, AO2, stack_pointer);
2291 assembleg_1(streamchar_gc, stack_pointer);
2292 AO2.value = GLULX_HEADER_SIZE+11;
2293 assembleg_2(copyb_gc, AO2, stack_pointer);
2294 assembleg_1(streamchar_gc, stack_pointer);
2296 if (/* DISABLES CODE */ (0)) {
2299 set_constant_ot(&AO);
2300 assembleg_1(streamchar_gc, AO);
2302 set_constant_ot(&AO);
2303 assembleg_1(streamchar_gc, AO);
2305 AO2.value = GLULX_HEADER_SIZE+12;
2306 assembleg_2(copyb_gc, AO2, stack_pointer);
2307 assembleg_1(streamchar_gc, stack_pointer);
2308 AO2.value = GLULX_HEADER_SIZE+13;
2309 assembleg_2(copyb_gc, AO2, stack_pointer);
2310 assembleg_1(streamchar_gc, stack_pointer);
2311 AO2.value = GLULX_HEADER_SIZE+14;
2312 assembleg_2(copyb_gc, AO2, stack_pointer);
2313 assembleg_1(streamchar_gc, stack_pointer);
2314 AO2.value = GLULX_HEADER_SIZE+15;
2315 assembleg_2(copyb_gc, AO2, stack_pointer);
2316 assembleg_1(streamchar_gc, stack_pointer);
2320 set_constant_ot(&AO);
2321 assembleg_1(streamchar_gc, AO);
2326 /* -------------------------------------------------------------------- */
2327 /* jump <label> ------------------------------------------------------- */
2328 /* -------------------------------------------------------------------- */
2331 assembleg_jump(parse_label());
2334 /* -------------------------------------------------------------------- */
2335 /* move <expression> to <expression> ---------------------------------- */
2336 /* -------------------------------------------------------------------- */
2339 misc_keywords.enabled = TRUE;
2340 AO = parse_expression(QUANTITY_CONTEXT);
2343 misc_keywords.enabled = FALSE;
2344 if ((token_type != MISC_KEYWORD_TT)
2345 || (token_value != TO_MK))
2346 { ebf_curtoken_error("'to'");
2347 panic_mode_error_recovery();
2351 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2352 QUANTITY_CONTEXT, -1);
2353 AO = code_generate(AO, QUANTITY_CONTEXT, -1);
2354 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
2355 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
2356 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
2357 assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2,
2360 assembleg_call_2(veneer_routine(OB__Move_VR), AO, AO2,
2364 /* -------------------------------------------------------------------- */
2365 /* new_line ----------------------------------------------------------- */
2366 /* -------------------------------------------------------------------- */
2369 INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
2370 assembleg_1(streamchar_gc, AO);
2373 /* -------------------------------------------------------------------- */
2374 /* objectloop (<initialisation>) <codeblock> -------------------------- */
2375 /* -------------------------------------------------------------------- */
2377 case OBJECTLOOP_CODE:
2379 match_open_bracket();
2381 if (token_type == LOCAL_VARIABLE_TT) {
2382 INITAOTV(&AO, LOCALVAR_OT, token_value);
2384 else if ((token_type == SYMBOL_TT) &&
2385 (symbols[token_value].type == GLOBAL_VARIABLE_T)) {
2386 INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
2389 ebf_curtoken_error("'objectloop' variable");
2390 panic_mode_error_recovery();
2393 misc_keywords.enabled = TRUE;
2394 get_next_token(); flag = TRUE;
2395 misc_keywords.enabled = FALSE;
2396 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
2400 if ((token_type == MISC_KEYWORD_TT)
2401 && (token_value == NEAR_MK)) ln = 1;
2402 if ((token_type == MISC_KEYWORD_TT)
2403 && (token_value == FROM_MK)) ln = 2;
2404 if ((token_type == CND_TT) && (token_value == IN_COND))
2407 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
2414 /* Old style (Inform 5) objectloops: note that we
2415 implement objectloop (a in b) in the old way since
2416 this runs through objects in a different order from
2417 the new way, and there may be existing Inform code
2419 assembly_operand AO4, AO5;
2422 sequence_point_follows = TRUE;
2423 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2424 QUANTITY_CONTEXT, -1);
2425 match_close_bracket();
2427 if (runtime_error_checking_switch)
2428 AO2 = check_nonzero_at_runtime(AO2, -1,
2430 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
2431 assembleg_3(aload_gc, AO2, AO4, stack_pointer);
2432 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
2433 assembleg_3(aload_gc, stack_pointer, AO4, stack_pointer);
2434 AO2 = stack_pointer;
2437 if (runtime_error_checking_switch) {
2439 AO2 = check_nonzero_at_runtime(AO2, -1,
2442 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
2443 assembleg_3(aload_gc, AO2, AO4, stack_pointer);
2444 AO2 = stack_pointer;
2449 assembleg_store(AO, AO2);
2450 assembleg_1_branch(jz_gc, AO, ln2 = next_label++);
2451 assemble_label_no(ln4 = next_label++);
2452 parse_code_block(ln2, ln3 = next_label++, 0);
2453 sequence_point_follows = FALSE;
2454 assemble_label_no(ln3);
2455 if (runtime_error_checking_switch) {
2456 AO2 = check_nonzero_at_runtime(AO, ln2,
2459 && ((AO5.type != LOCALVAR_OT)||(AO5.value != 0))
2460 && ((AO5.type != LOCALVAR_OT)||(AO5.value != AO.value)))
2461 { assembly_operand en_ao;
2463 en_ao.value = OBJECTLOOP_BROKEN_RTE;
2464 set_constant_ot(&en_ao);
2465 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
2466 assembleg_3(aload_gc, AO, AO4, stack_pointer);
2467 assembleg_2_branch(jeq_gc, stack_pointer, AO5,
2469 assembleg_call_2(veneer_routine(RT__Err_VR),
2470 en_ao, AO, zero_operand);
2471 assembleg_jump(ln2);
2472 assemble_label_no(next_label++);
2478 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_SIBLING());
2479 assembleg_3(aload_gc, AO2, AO4, AO);
2480 assembleg_1_branch(jnz_gc, AO, ln4);
2481 assemble_label_no(ln2);
2485 sequence_point_follows = TRUE;
2486 ln = get_symbol_index("Class");
2488 error("No 'Class' object found");
2492 INITAOT(&AO2, CONSTANT_OT);
2493 AO2.value = symbols[ln].value;
2494 AO2.marker = OBJECT_MV;
2496 assembleg_store(AO, AO2);
2498 assemble_label_no(ln = next_label++);
2504 sequence_point_follows = TRUE;
2505 code_generate(parse_expression(CONDITION_CONTEXT),
2506 CONDITION_CONTEXT, ln3);
2507 match_close_bracket();
2509 parse_code_block(ln2, ln3, 0);
2511 sequence_point_follows = FALSE;
2512 assemble_label_no(ln3);
2513 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHAIN());
2514 assembleg_3(aload_gc, AO, AO4, AO);
2515 assembleg_1_branch(jnz_gc, AO, ln);
2516 assemble_label_no(ln2);
2519 /* -------------------------------------------------------------------- */
2520 /* (see routine above) ------------------------------------------------ */
2521 /* -------------------------------------------------------------------- */
2525 parse_print_g(FALSE); return;
2526 case PRINT_RET_CODE:
2528 parse_print_g(TRUE); return;
2530 /* -------------------------------------------------------------------- */
2531 /* quit --------------------------------------------------------------- */
2532 /* -------------------------------------------------------------------- */
2535 assembleg_0(quit_gc); break;
2537 /* -------------------------------------------------------------------- */
2538 /* remove <expression> ------------------------------------------------ */
2539 /* -------------------------------------------------------------------- */
2542 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2543 QUANTITY_CONTEXT, -1);
2544 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
2545 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
2546 assembleg_call_1(veneer_routine(RT__ChR_VR), AO,
2549 assembleg_call_1(veneer_routine(OB__Remove_VR), AO,
2553 /* -------------------------------------------------------------------- */
2554 /* return [<expression>] ---------------------------------------------- */
2555 /* -------------------------------------------------------------------- */
2559 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) {
2560 assembleg_1(return_gc, one_operand);
2564 AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
2565 QUANTITY_CONTEXT, -1);
2566 assembleg_1(return_gc, AO);
2569 /* -------------------------------------------------------------------- */
2570 /* rfalse ------------------------------------------------------------- */
2571 /* -------------------------------------------------------------------- */
2574 assembleg_1(return_gc, zero_operand);
2577 /* -------------------------------------------------------------------- */
2578 /* rtrue -------------------------------------------------------------- */
2579 /* -------------------------------------------------------------------- */
2582 assembleg_1(return_gc, one_operand);
2585 /* -------------------------------------------------------------------- */
2586 /* spaces <expression> ------------------------------------------------ */
2587 /* -------------------------------------------------------------------- */
2590 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2591 QUANTITY_CONTEXT, -1);
2593 assembleg_store(temp_var1, AO);
2596 AO.value = 32; set_constant_ot(&AO);
2598 assembleg_2_branch(jlt_gc, temp_var1, one_operand,
2600 assemble_label_no(ln2 = next_label++);
2601 assembleg_1(streamchar_gc, AO);
2602 assembleg_dec(temp_var1);
2603 assembleg_1_branch(jnz_gc, temp_var1, ln2);
2604 assemble_label_no(ln);
2607 /* -------------------------------------------------------------------- */
2608 /* string <expression> <literal-string> ------------------------------- */
2609 /* -------------------------------------------------------------------- */
2612 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2613 QUANTITY_CONTEXT, -1);
2614 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
2615 /* Compile-time check */
2616 if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
2617 error_max_dynamic_strings(AO2.value);
2621 if (token_type == DQ_TT)
2622 { INITAOT(&AO4, CONSTANT_OT);
2623 /* This is not actually placed in low memory; Glulx
2624 has no such concept. We use the LOWSTRING flag
2625 for compatibility with older compiler behavior. */
2626 AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
2627 AO4.marker = STRING_MV;
2631 AO4 = parse_expression(CONSTANT_CONTEXT);
2633 assembleg_call_2(veneer_routine(Dynam__String_VR),
2634 AO2, AO4, zero_operand);
2637 /* -------------------------------------------------------------------- */
2638 /* style roman/reverse/bold/underline/fixed --------------------------- */
2639 /* -------------------------------------------------------------------- */
2642 misc_keywords.enabled = TRUE;
2644 misc_keywords.enabled = FALSE;
2645 if ((token_type != MISC_KEYWORD_TT)
2646 || ((token_value != ROMAN_MK)
2647 && (token_value != REVERSE_MK)
2648 && (token_value != BOLD_MK)
2649 && (token_value != UNDERLINE_MK)
2650 && (token_value != FIXED_MK)))
2651 { ebf_curtoken_error(
2652 "'roman', 'bold', 'underline', 'reverse' or 'fixed'");
2653 panic_mode_error_recovery();
2657 /* Call glk_set_style() */
2661 set_constant_ot(&AO);
2665 AO2 = zero_operand; /* normal */
2669 AO2.value = 5; /* alert */
2670 set_constant_ot(&AO2);
2674 AO2.value = 4; /* subheader */
2675 set_constant_ot(&AO2);
2678 AO2 = one_operand; /* emphasized */
2681 AO2 = two_operand; /* preformatted */
2684 assembleg_call_2(veneer_routine(Glk__Wrap_VR),
2685 AO, AO2, zero_operand);
2688 /* -------------------------------------------------------------------- */
2689 /* switch (<expression>) <codeblock> ---------------------------------- */
2690 /* -------------------------------------------------------------------- */
2693 match_open_bracket();
2694 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2695 QUANTITY_CONTEXT, -1);
2696 match_close_bracket();
2698 assembleg_store(temp_var1, AO);
2700 parse_code_block(ln = next_label++, continue_label, 1);
2701 assemble_forward_label_no(ln);
2704 /* -------------------------------------------------------------------- */
2705 /* while (<condition>) <codeblock> ------------------------------------ */
2706 /* -------------------------------------------------------------------- */
2709 assemble_label_no(ln = next_label++);
2710 match_open_bracket();
2712 code_generate(parse_expression(CONDITION_CONTEXT),
2713 CONDITION_CONTEXT, ln2 = next_label++);
2714 match_close_bracket();
2716 parse_code_block(ln2, ln, 0);
2717 sequence_point_follows = FALSE;
2719 assemble_forward_label_no(ln2);
2722 /* -------------------------------------------------------------------- */
2725 error("'default' without matching 'switch'"); break;
2727 error("'else' without matching 'if'"); break;
2729 error("'until' without matching 'do'");
2730 panic_mode_error_recovery(); return;
2732 /* -------------------------------------------------------------------- */
2734 /* And a useful default, which will never be triggered in a complete
2735 Inform compiler, but which is important in development. */
2738 error("*** Statement code gen: Can't generate yet ***\n");
2739 panic_mode_error_recovery(); return;
2742 StatementTerminator:
2745 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
2746 { ebf_curtoken_error("';'");
2751 extern void parse_statement(int break_label, int continue_label)
2754 int saved_entire_flag;
2756 res = parse_named_label_statements();
2760 saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
2761 if (execution_never_reaches_here)
2762 execution_never_reaches_here |= EXECSTATE_ENTIRE;
2765 parse_statement_z(break_label, continue_label);
2767 parse_statement_g(break_label, continue_label);
2769 if (saved_entire_flag)
2770 execution_never_reaches_here |= EXECSTATE_ENTIRE;
2772 execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
2775 /* This does the same work as parse_statement(), but it's called if you've
2776 already parsed an expression (in void context) and you want to generate
2777 it as a statement. Essentially it's a copy of parse_statement() and
2778 parse_statement_z/g(), except we skip straight to the "expression-with-
2779 side-effects" bit and omit everything else.
2781 The caller doesn't need to pass break_label/continue_label; they're
2782 not used for this code path.
2784 extern void parse_statement_singleexpr(assembly_operand AO)
2787 int saved_entire_flag;
2789 res = parse_named_label_statements();
2793 saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
2794 if (execution_never_reaches_here)
2795 execution_never_reaches_here |= EXECSTATE_ENTIRE;
2797 code_generate(AO, VOID_CONTEXT, -1);
2800 panic_mode_error_recovery();
2803 /* StatementTerminator... */
2805 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
2806 { ebf_curtoken_error("';'");
2811 if (saved_entire_flag)
2812 execution_never_reaches_here |= EXECSTATE_ENTIRE;
2814 execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
2817 /* ========================================================================= */
2818 /* Data structure management routines */
2819 /* ------------------------------------------------------------------------- */
2821 extern void init_states_vars(void)
2825 extern void states_begin_pass(void)
2829 extern void states_allocate_arrays(void)
2833 extern void states_free_arrays(void)
2837 /* ========================================================================= */