1 /* ------------------------------------------------------------------------- */
2 /* "states" : Statement translator */
4 /* Part of Inform 6.41 */
5 /* copyright (c) Graham Nelson 1993 - 2022 */
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_error("':'", token_text);
33 panic_mode_error_recovery();
38 { ebf_error("':'", token_text);
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_error("'('", token_text);
52 extern void match_close_bracket(void)
54 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP)) return;
56 ebf_error("')'", token_text);
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;
101 { codegen_action = FALSE;
102 AO2 = action_of_name(token_text);
109 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
112 AO3 = parse_expression(ACTION_Q_CONTEXT);
116 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
119 AO4 = parse_expression(QUANTITY_CONTEXT);
122 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
124 ebf_error("',' or '>'", token_text);
127 if ((token_type == SEP_TT) && (token_value == COMMA_SEP))
129 if (!glulx_mode && (version_number < 4))
131 error("<x, y> syntax is not available in Z-code V3 or earlier");
134 AO5 = parse_expression(QUANTITY_CONTEXT);
136 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
138 ebf_error("'>'", token_text);
144 if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
146 ebf_error("'>>'", token_text);
152 AO = veneer_routine(R_Process_VR);
156 if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
157 if (version_number>=5)
158 assemblez_2(call_2n_zc, AO, AO2);
160 if (version_number==4)
161 assemblez_2_to(call_vs_zc, AO, AO2, temp_var1);
163 assemblez_2_to(call_zc, AO, AO2, temp_var1);
166 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
167 if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
168 if (version_number>=5)
169 assemblez_3(call_vn_zc, AO, AO2, AO3);
171 if (version_number==4)
172 assemblez_3_to(call_vs_zc, AO, AO2, AO3, temp_var1);
174 assemblez_3_to(call_zc, AO, AO2, AO3, temp_var1);
177 AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
178 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
179 if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
180 if (version_number>=5)
181 assemblez_4(call_vn_zc, AO, AO2, AO3, AO4);
183 if (version_number==4)
184 assemblez_4_to(call_vs_zc, AO, AO2, AO3, AO4, temp_var1);
186 assemblez_4_to(call_zc, AO, AO2, AO3, AO4, temp_var1);
189 AO5 = code_generate(AO5, QUANTITY_CONTEXT, -1);
190 AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
191 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
192 if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
193 if (version_number>=5)
194 assemblez_5(call_vn2_zc, AO, AO2, AO3, AO4, AO5);
196 if (version_number==4)
197 assemblez_5_to(call_vs2_zc, AO, AO2, AO3, AO4, AO5, temp_var1);
198 /* if V3 or earlier, we've already displayed an error */
203 if (level == 2) assemblez_0(rtrue_zc);
208 AO = veneer_routine(R_Process_VR);
214 AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
215 assembleg_call_1(AO, AO2, zero_operand);
219 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
221 AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
222 assembleg_call_2(AO, AO2, AO3, zero_operand);
226 AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
227 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
229 AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
230 assembleg_call_3(AO, AO2, AO3, AO4, zero_operand);
234 AO5 = code_generate(AO5, QUANTITY_CONTEXT, -1);
235 if (!((AO5.type == LOCALVAR_OT) && (AO5.value == 0)))
236 assembleg_store(stack_pointer, AO5);
237 AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
238 if (!((AO4.type == LOCALVAR_OT) && (AO4.value == 0)))
239 assembleg_store(stack_pointer, AO4);
240 AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
241 if (!((AO3.type == LOCALVAR_OT) && (AO3.value == 0)))
242 assembleg_store(stack_pointer, AO3);
244 AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
245 if (!((AO2.type == LOCALVAR_OT) && (AO2.value == 0)))
246 assembleg_store(stack_pointer, AO2);
247 assembleg_3(call_gc, AO, four_operand, zero_operand);
252 assembleg_1(return_gc, one_operand);
257 extern int parse_label(void)
261 if ((token_type == SYMBOL_TT) &&
262 (symbols[token_value].type == LABEL_T))
263 { symbols[token_value].flags |= USED_SFLAG;
264 return(symbols[token_value].value);
267 if ((token_type == SYMBOL_TT) && (symbols[token_value].flags & UNKNOWN_SFLAG))
268 { assign_symbol(token_value, next_label, LABEL_T);
269 define_symbol_label(token_value);
271 symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG;
272 return(symbols[token_value].value);
275 ebf_error("label name", token_text);
279 static void parse_print_z(int finally_return)
280 { int count = 0; assembly_operand AO;
282 /* print <printlist> -------------------------------------------------- */
283 /* print_ret <printlist> ---------------------------------------------- */
284 /* <literal-string> --------------------------------------------------- */
286 /* <printlist> is a comma-separated list of items: */
288 /* <literal-string> */
289 /* <other-expression> */
290 /* (char) <expression> */
291 /* (address) <expression> */
292 /* (string) <expression> */
293 /* (a) <expression> */
294 /* (the) <expression> */
295 /* (The) <expression> */
296 /* (name) <expression> */
297 /* (number) <expression> */
298 /* (property) <expression> */
299 /* (<routine>) <expression> */
300 /* (object) <expression> (for use in low-level code only) */
301 /* --------------------------------------------------------------------- */
304 { AI.text = token_text;
305 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
308 if (strlen(token_text) > 32)
309 { INITAOT(&AO, LONG_CONSTANT_OT);
310 AO.marker = STRING_MV;
311 AO.value = compile_string(token_text, STRCTX_GAME);
312 assemblez_1(print_paddr_zc, AO);
315 if ((token_type == SEP_TT)
316 && (token_value == SEMICOLON_SEP))
317 { assemblez_0(new_line_zc);
318 assemblez_0(rtrue_zc);
327 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
328 { assemblez_0(print_ret_zc); return;
332 assemblez_0(print_zc);
336 if (token_value == OPENB_SEP)
337 { misc_keywords.enabled = TRUE;
340 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
341 { assembly_operand AO1;
343 put_token_back(); put_token_back();
344 local_variables.enabled = FALSE;
346 misc_keywords.enabled = FALSE;
347 local_variables.enabled = TRUE;
349 if ((token_type == STATEMENT_TT)
350 &&(token_value == STRING_CODE))
351 { token_type = MISC_KEYWORD_TT;
352 token_value = STRING_MK;
357 case MISC_KEYWORD_TT:
360 if (runtime_error_checking_switch)
361 { AO = veneer_routine(RT__ChPrintC_VR);
366 parse_expression(QUANTITY_CONTEXT),
367 QUANTITY_CONTEXT, -1);
368 assemblez_1(print_char_zc, AO1);
371 if (runtime_error_checking_switch)
372 { AO = veneer_routine(RT__ChPrintA_VR);
377 parse_expression(QUANTITY_CONTEXT),
378 QUANTITY_CONTEXT, -1);
379 assemblez_1(print_addr_zc, AO1);
382 if (runtime_error_checking_switch)
383 { AO = veneer_routine(RT__ChPrintS_VR);
388 parse_expression(QUANTITY_CONTEXT),
389 QUANTITY_CONTEXT, -1);
390 assemblez_1(print_paddr_zc, AO1);
393 if (runtime_error_checking_switch)
394 { AO = veneer_routine(RT__ChPrintO_VR);
399 parse_expression(QUANTITY_CONTEXT),
400 QUANTITY_CONTEXT, -1);
401 assemblez_1(print_obj_zc, AO1);
404 AO = veneer_routine(DefArt_VR);
408 AO = veneer_routine(InDefArt_VR);
411 AO = veneer_routine(CDefArt_VR);
414 AO = veneer_routine(CInDefArt_VR);
417 AO = veneer_routine(PrintShortName_VR);
420 AO = veneer_routine(EnglishNumber_VR);
423 AO = veneer_routine(Print__Pname_VR);
426 error_named("A reserved word was used as a print specification:",
432 if (symbols[token_value].flags & UNKNOWN_SFLAG)
433 { INITAOT(&AO, LONG_CONSTANT_OT);
434 AO.value = token_value;
435 AO.marker = SYMBOL_MV;
436 AO.symindex = token_value;
439 { INITAOT(&AO, LONG_CONSTANT_OT);
440 AO.value = symbols[token_value].value;
441 AO.marker = IROUTINE_MV;
442 AO.symindex = token_value;
443 if (symbols[token_value].type != ROUTINE_T)
444 ebf_error("printing routine name", token_text);
446 symbols[token_value].flags |= USED_SFLAG;
451 if (version_number >= 5)
452 assemblez_2(call_2n_zc, AO,
453 code_generate(parse_expression(QUANTITY_CONTEXT),
454 QUANTITY_CONTEXT, -1));
455 else if (version_number == 4)
456 assemblez_2_to(call_vs_zc, AO,
457 code_generate(parse_expression(QUANTITY_CONTEXT),
458 QUANTITY_CONTEXT, -1), temp_var1);
460 assemblez_2_to(call_zc, AO,
461 code_generate(parse_expression(QUANTITY_CONTEXT),
462 QUANTITY_CONTEXT, -1), temp_var1);
465 default: ebf_error("print specification", token_text);
467 assemblez_1(print_num_zc,
468 code_generate(parse_expression(QUANTITY_CONTEXT),
469 QUANTITY_CONTEXT, -1));
473 put_token_back(); put_token_back(); put_token_back();
474 misc_keywords.enabled = FALSE;
475 assemblez_1(print_num_zc,
476 code_generate(parse_expression(QUANTITY_CONTEXT),
477 QUANTITY_CONTEXT, -1));
482 put_token_back(); misc_keywords.enabled = FALSE;
483 assemblez_1(print_num_zc,
484 code_generate(parse_expression(QUANTITY_CONTEXT),
485 QUANTITY_CONTEXT, -1));
489 PrintTermDone: misc_keywords.enabled = FALSE;
493 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
494 if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
495 { ebf_error("comma", token_text);
496 panic_mode_error_recovery(); return;
498 else get_next_token();
501 if (count == 0) ebf_error("something to print", token_text);
503 { assemblez_0(new_line_zc);
504 assemblez_0(rtrue_zc);
508 static void parse_print_g(int finally_return)
509 { int count = 0; assembly_operand AO, AO2;
511 /* print <printlist> -------------------------------------------------- */
512 /* print_ret <printlist> ---------------------------------------------- */
513 /* <literal-string> --------------------------------------------------- */
515 /* <printlist> is a comma-separated list of items: */
517 /* <literal-string> */
518 /* <other-expression> */
519 /* (char) <expression> */
520 /* (address) <expression> */
521 /* (string) <expression> */
522 /* (a) <expression> */
523 /* (A) <expression> */
524 /* (the) <expression> */
525 /* (The) <expression> */
526 /* (name) <expression> */
527 /* (number) <expression> */
528 /* (property) <expression> */
529 /* (<routine>) <expression> */
530 /* (object) <expression> (for use in low-level code only) */
531 /* --------------------------------------------------------------------- */
535 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
538 /* We can't compile a string into the instruction,
539 so this always goes into the string area. */
540 { INITAOT(&AO, CONSTANT_OT);
541 AO.marker = STRING_MV;
542 AO.value = compile_string(token_text, STRCTX_GAME);
543 assembleg_1(streamstr_gc, AO);
546 if ((token_type == SEP_TT)
547 && (token_value == SEMICOLON_SEP))
548 { INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
549 assembleg_1(streamchar_gc, AO);
550 INITAOTV(&AO, BYTECONSTANT_OT, 1);
551 assembleg_1(return_gc, AO);
561 if (token_value == OPENB_SEP)
562 { misc_keywords.enabled = TRUE;
565 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
566 { assembly_operand AO1;
569 put_token_back(); put_token_back();
570 local_variables.enabled = FALSE;
572 misc_keywords.enabled = FALSE;
573 local_variables.enabled = TRUE;
575 if ((token_type == STATEMENT_TT)
576 &&(token_value == STRING_CODE))
577 { token_type = MISC_KEYWORD_TT;
578 token_value = STRING_MK;
583 case MISC_KEYWORD_TT:
586 if (runtime_error_checking_switch)
587 { AO = veneer_routine(RT__ChPrintC_VR);
592 parse_expression(QUANTITY_CONTEXT),
593 QUANTITY_CONTEXT, -1);
594 if ((AO1.type == LOCALVAR_OT) && (AO1.value == 0))
595 { assembleg_2(stkpeek_gc, zero_operand,
598 INITAOTV(&AO2, HALFCONSTANT_OT, 0x100);
599 assembleg_2_branch(jgeu_gc, AO1, AO2,
602 assembleg_1(streamchar_gc, AO1);
604 assemble_label_no(ln);
605 assembleg_1(streamunichar_gc, AO1);
606 assemble_label_no(ln2);
609 if (runtime_error_checking_switch)
610 AO = veneer_routine(RT__ChPrintA_VR);
612 AO = veneer_routine(Print__Addr_VR);
615 if (runtime_error_checking_switch)
616 { AO = veneer_routine(RT__ChPrintS_VR);
621 parse_expression(QUANTITY_CONTEXT),
622 QUANTITY_CONTEXT, -1);
623 assembleg_1(streamstr_gc, AO1);
626 if (runtime_error_checking_switch)
627 { AO = veneer_routine(RT__ChPrintO_VR);
632 parse_expression(QUANTITY_CONTEXT),
633 QUANTITY_CONTEXT, -1);
634 INITAOT(&AO2, BYTECONSTANT_OT);
635 AO2.value = GOBJFIELD_NAME();
636 assembleg_3(aload_gc, AO1, AO2,
638 assembleg_1(streamstr_gc, stack_pointer);
641 AO = veneer_routine(DefArt_VR);
645 AO = veneer_routine(InDefArt_VR);
648 AO = veneer_routine(CDefArt_VR);
651 AO = veneer_routine(CInDefArt_VR);
654 AO = veneer_routine(PrintShortName_VR);
657 AO = veneer_routine(EnglishNumber_VR);
660 AO = veneer_routine(Print__Pname_VR);
663 error_named("A reserved word was used as a print specification:",
669 if (symbols[token_value].flags & UNKNOWN_SFLAG)
670 { INITAOT(&AO, CONSTANT_OT);
671 AO.value = token_value;
672 AO.marker = SYMBOL_MV;
673 AO.symindex = token_value;
676 { INITAOT(&AO, CONSTANT_OT);
677 AO.value = symbols[token_value].value;
678 AO.marker = IROUTINE_MV;
679 AO.symindex = token_value;
680 if (symbols[token_value].type != ROUTINE_T)
681 ebf_error("printing routine name", token_text);
683 symbols[token_value].flags |= USED_SFLAG;
688 INITAOT(&AO2, ZEROCONSTANT_OT);
690 code_generate(parse_expression(QUANTITY_CONTEXT),
691 QUANTITY_CONTEXT, -1),
695 default: ebf_error("print specification", token_text);
697 assembleg_1(streamnum_gc,
698 code_generate(parse_expression(QUANTITY_CONTEXT),
699 QUANTITY_CONTEXT, -1));
703 put_token_back(); put_token_back(); put_token_back();
704 misc_keywords.enabled = FALSE;
705 assembleg_1(streamnum_gc,
706 code_generate(parse_expression(QUANTITY_CONTEXT),
707 QUANTITY_CONTEXT, -1));
712 put_token_back(); misc_keywords.enabled = FALSE;
713 assembleg_1(streamnum_gc,
714 code_generate(parse_expression(QUANTITY_CONTEXT),
715 QUANTITY_CONTEXT, -1));
719 PrintTermDone: misc_keywords.enabled = FALSE;
723 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
724 if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
725 { ebf_error("comma", token_text);
726 panic_mode_error_recovery(); return;
728 else get_next_token();
731 if (count == 0) ebf_error("something to print", token_text);
734 INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
735 assembleg_1(streamchar_gc, AO);
736 INITAOTV(&AO, BYTECONSTANT_OT, 1);
737 assembleg_1(return_gc, AO);
741 /* Parse any number of ".Label;" lines before a statement.
742 Returns whether a statement can in fact follow. */
743 static int parse_named_label_statements()
745 while ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
746 { /* That is, a full stop, signifying a label */
749 if (token_type != SYMBOL_TT)
751 ebf_error("label name", token_text);
755 if (symbols[token_value].flags & UNKNOWN_SFLAG)
756 { assign_symbol(token_value, next_label, LABEL_T);
757 symbols[token_value].flags |= USED_SFLAG;
758 assemble_label_no(next_label);
759 define_symbol_label(token_value);
763 { if (symbols[token_value].type != LABEL_T) {
764 ebf_error("label name", token_text);
767 if (symbols[token_value].flags & CHANGE_SFLAG)
768 { symbols[token_value].flags &= (~(CHANGE_SFLAG));
769 assemble_label_no(symbols[token_value].value);
770 define_symbol_label(token_value);
772 else error_named("Duplicate definition of label:", token_text);
776 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
777 { ebf_error("';'", token_text);
778 put_token_back(); return FALSE;
781 /* Interesting point of Inform grammar: a statement can only
782 consist solely of a label when it is immediately followed
786 if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
787 { put_token_back(); return FALSE;
789 /* The following line prevents labels from influencing the positions
790 of sequence points. */
791 statement_debug_location = get_token_location();
793 /* Another label might follow */
796 /* On with the statement */
800 static void parse_statement_z(int break_label, int continue_label)
801 { int ln, ln2, ln3, ln4, flag;
802 int pre_unreach, labelexists;
803 assembly_operand AO, AO2, AO3, AO4;
804 debug_location spare_debug_location1, spare_debug_location2;
808 if ((token_type == SEP_TT) && (token_value == HASH_SEP))
809 { parse_directive(TRUE);
810 parse_statement(break_label, continue_label); return;
813 if ((token_type == SEP_TT) && (token_value == AT_SEP))
814 { parse_assembly(); return;
817 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
819 if (token_type == DQ_TT)
820 { parse_print_z(TRUE); return;
823 if ((token_type == SEP_TT) && (token_value == LESS_SEP))
824 { parse_action(); goto StatementTerminator; }
826 if (token_type == EOF_TT)
827 { ebf_error("statement", token_text); return; }
829 if (token_type != STATEMENT_TT)
831 AO = parse_expression(VOID_CONTEXT);
832 code_generate(AO, VOID_CONTEXT, -1);
833 if (vivc_flag) { panic_mode_error_recovery(); return; }
834 goto StatementTerminator;
837 statements.enabled = FALSE;
841 /* -------------------------------------------------------------------- */
842 /* box <string-1> ... <string-n> -------------------------------------- */
843 /* -------------------------------------------------------------------- */
846 if (version_number == 3)
847 warning("The 'box' statement has no effect in a version 3 game");
848 INITAOT(&AO3, LONG_CONSTANT_OT);
849 AO3.value = begin_table_array();
850 AO3.marker = ARRAY_MV;
854 if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
856 if (token_type != DQ_TT)
857 ebf_error("text of box line in double-quotes",
860 for (i=0, j=0; token_text[i] != 0; j++)
861 if (token_text[i] == '@')
862 { if (token_text[i+1] == '@')
864 while (isdigit(token_text[i])) i++;
868 if (token_text[i] != 0) i++;
869 if (token_text[i] != 0) i++;
873 if (j > ln2) ln2 = j;
876 array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
878 finish_array(ln, FALSE);
880 error("No lines of text given for 'box' display");
882 if (version_number == 3) return;
884 INITAOTV(&AO2, SHORT_CONSTANT_OT, ln2);
885 INITAOTV(&AO4, VARIABLE_OT, 255);
886 assemblez_3_to(call_vs_zc, veneer_routine(Box__Routine_VR),
890 /* -------------------------------------------------------------------- */
891 /* break -------------------------------------------------------------- */
892 /* -------------------------------------------------------------------- */
895 if (break_label == -1)
896 error("'break' can only be used in a loop or 'switch' block");
898 assemblez_jump(break_label);
901 /* -------------------------------------------------------------------- */
902 /* continue ----------------------------------------------------------- */
903 /* -------------------------------------------------------------------- */
906 if (continue_label == -1)
907 error("'continue' can only be used in a loop block");
909 assemblez_jump(continue_label);
912 /* -------------------------------------------------------------------- */
913 /* do <codeblock> until (<condition>) --------------------------------- */
914 /* -------------------------------------------------------------------- */
917 assemble_label_no(ln = next_label++);
918 ln2 = next_label++; ln3 = next_label++;
919 parse_code_block(ln3, ln2, 0);
920 statements.enabled = TRUE;
922 if ((token_type == STATEMENT_TT)
923 && (token_value == UNTIL_CODE))
924 { assemble_forward_label_no(ln2);
925 match_open_bracket();
926 AO = parse_expression(CONDITION_CONTEXT);
927 match_close_bracket();
928 code_generate(AO, CONDITION_CONTEXT, ln);
930 else error("'do' without matching 'until'");
932 assemble_forward_label_no(ln3);
935 /* -------------------------------------------------------------------- */
936 /* font on/off -------------------------------------------------------- */
937 /* -------------------------------------------------------------------- */
940 misc_keywords.enabled = TRUE;
942 misc_keywords.enabled = FALSE;
943 if ((token_type != MISC_KEYWORD_TT)
944 || ((token_value != ON_MK)
945 && (token_value != OFF_MK)))
946 { ebf_error("'on' or 'off'", token_text);
947 panic_mode_error_recovery();
951 if (version_number >= 5)
952 { /* Use the V5 @set_font opcode, setting font 4
953 (for font off) or 1 (for font on). */
954 INITAOT(&AO, SHORT_CONSTANT_OT);
955 if (token_value == ON_MK)
959 assemblez_1_to(set_font_zc, AO, temp_var1);
963 /* Set the fixed-pitch header bit. */
964 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
965 INITAOTV(&AO2, SHORT_CONSTANT_OT, 8);
966 INITAOTV(&AO3, VARIABLE_OT, 255);
967 assemblez_2_to(loadw_zc, AO, AO2, AO3);
969 if (token_value == ON_MK)
970 { INITAOTV(&AO4, LONG_CONSTANT_OT, 0xfffd);
971 assemblez_2_to(and_zc, AO4, AO3, AO3);
974 { INITAOTV(&AO4, SHORT_CONSTANT_OT, 2);
975 assemblez_2_to(or_zc, AO4, AO3, AO3);
978 assemblez_3(storew_zc, AO, AO2, AO3);
981 /* -------------------------------------------------------------------- */
982 /* for (<initialisation> : <continue-condition> : <updating>) --------- */
983 /* -------------------------------------------------------------------- */
985 /* Note that it's legal for any or all of the three sections of a
986 'for' specification to be empty. This 'for' implementation
987 often wastes 3 bytes with a redundant branch rather than keep
988 expression parse trees for long periods (as previous versions
989 of Inform did, somewhat crudely by simply storing the textual
990 form of a 'for' loop). It is adequate for now. */
993 match_open_bracket();
996 /* Initialisation code */
997 AO.type = OMITTED_OT;
998 spare_debug_location1 = statement_debug_location;
999 AO2.type = OMITTED_OT; flag = 0;
1000 spare_debug_location2 = statement_debug_location;
1002 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1004 if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
1005 { sequence_point_follows = TRUE;
1006 statement_debug_location = get_token_location();
1007 code_generate(parse_expression(FORINIT_CONTEXT),
1011 if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
1013 if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
1014 { assemble_label_no(ln = next_label++);
1016 parse_code_block(ln2, ln, 0);
1017 sequence_point_follows = FALSE;
1018 if (!execution_never_reaches_here)
1020 assemble_forward_label_no(ln2);
1026 if (!match_colon()) break;
1030 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1032 spare_debug_location1 = get_token_location();
1033 AO = parse_expression(CONDITION_CONTEXT);
1034 if (!match_colon()) break;
1039 if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
1041 spare_debug_location2 = get_token_location();
1042 AO2 = parse_expression(VOID_CONTEXT);
1043 match_close_bracket();
1044 flag = test_for_incdec(AO2);
1051 if ((AO2.type == OMITTED_OT) || (flag != 0))
1053 assemble_label_no(ln);
1054 if (flag==0) assemble_label_no(ln2);
1056 /* The "finished yet?" condition */
1058 if (AO.type != OMITTED_OT)
1059 { sequence_point_follows = TRUE;
1060 statement_debug_location = spare_debug_location1;
1061 code_generate(AO, CONDITION_CONTEXT, ln3);
1067 /* This is the jump which could be avoided with the aid
1068 of long-term expression storage */
1070 sequence_point_follows = FALSE;
1071 assemblez_jump(ln2);
1073 /* The "update" part */
1075 assemble_label_no(ln);
1076 sequence_point_follows = TRUE;
1077 statement_debug_location = spare_debug_location2;
1078 code_generate(AO2, VOID_CONTEXT, -1);
1080 assemble_label_no(ln2);
1082 /* The "finished yet?" condition */
1084 if (AO.type != OMITTED_OT)
1085 { sequence_point_follows = TRUE;
1086 statement_debug_location = spare_debug_location1;
1087 code_generate(AO, CONDITION_CONTEXT, ln3);
1093 /* In this optimised case, update code is at the end
1094 of the loop block, so "continue" goes there */
1096 parse_code_block(ln3, ln2, 0);
1097 assemble_label_no(ln2);
1099 sequence_point_follows = TRUE;
1100 statement_debug_location = spare_debug_location2;
1102 { INITAOTV(&AO3, SHORT_CONSTANT_OT, flag);
1103 assemblez_1(inc_zc, AO3);
1106 { INITAOTV(&AO3, SHORT_CONSTANT_OT, -flag);
1107 assemblez_1(dec_zc, AO3);
1113 /* In the unoptimised case, update code is at the
1114 start of the loop block, so "continue" goes there */
1116 parse_code_block(ln3, ln, 0);
1117 if (!execution_never_reaches_here)
1118 { sequence_point_follows = FALSE;
1123 assemble_forward_label_no(ln3);
1126 /* -------------------------------------------------------------------- */
1127 /* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
1128 /* -------------------------------------------------------------------- */
1131 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1132 QUANTITY_CONTEXT, -1);
1133 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
1134 if ((AO.type == VARIABLE_OT) && (AO.value == 0))
1135 { INITAOTV(&AO, SHORT_CONSTANT_OT, 252);
1136 if (version_number != 6) assemblez_1(pull_zc, AO);
1137 else assemblez_0_to(pull_zc, AO);
1138 AO.type = VARIABLE_OT;
1143 if ((token_type == SEP_TT)&&(token_value == SEMICOLON_SEP))
1145 if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
1151 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1152 QUANTITY_CONTEXT, -1);
1153 check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
1154 if (runtime_error_checking_switch)
1155 { ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
1156 if (version_number >= 5)
1157 assemblez_3(call_vn_zc, veneer_routine(ln2),
1161 assemblez_3_to(call_zc, veneer_routine(ln2),
1162 AO, AO2, temp_var1);
1166 assemblez_2(ln, AO, AO2);
1169 /* -------------------------------------------------------------------- */
1170 /* if (<condition>) <codeblock> [else <codeblock>] -------------------- */
1171 /* -------------------------------------------------------------------- */
1174 flag = FALSE; /* set if there's an "else" */
1176 pre_unreach = execution_never_reaches_here;
1178 match_open_bracket();
1179 AO = parse_expression(CONDITION_CONTEXT);
1180 match_close_bracket();
1182 statements.enabled = TRUE;
1184 if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
1187 if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
1195 code_generate(AO, CONDITION_CONTEXT, ln);
1197 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
1198 /* If the condition never falls through to here, then
1199 it was an "if (0)" test. Our convention is to skip
1200 the "not reached" warnings for this case. */
1201 execution_never_reaches_here |= EXECSTATE_NOWARN;
1204 /* The "if" block */
1205 if (ln >= 0) parse_code_block(break_label, continue_label, 0);
1208 if ((token_type != SEP_TT)
1209 || (token_value != SEMICOLON_SEP))
1210 { ebf_error("';'", token_text);
1215 statements.enabled = TRUE;
1218 /* An #if directive around the ELSE clause is legal. */
1219 while ((token_type == SEP_TT) && (token_value == HASH_SEP))
1220 { parse_directive(TRUE);
1221 statements.enabled = TRUE;
1225 if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
1228 { ln2 = next_label++;
1229 if (!execution_never_reaches_here)
1230 { sequence_point_follows = FALSE;
1231 assemblez_jump(ln2);
1235 else put_token_back();
1237 /* The "else" label (or end of statement, if there is no "else") */
1238 labelexists = FALSE;
1239 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
1243 /* If labelexists is false, then we started with
1244 "if (1)". In this case, we don't want a "not
1245 reached" warning on the "else" block. We
1246 temporarily disable the NOWARN flag, and restore it
1248 int saved_unreach = 0;
1249 if (execution_never_reaches_here && !labelexists) {
1250 saved_unreach = execution_never_reaches_here;
1251 execution_never_reaches_here |= EXECSTATE_NOWARN;
1254 /* The "else" block */
1255 parse_code_block(break_label, continue_label, 0);
1257 if (execution_never_reaches_here && !labelexists) {
1258 if (saved_unreach & EXECSTATE_NOWARN)
1259 execution_never_reaches_here |= EXECSTATE_NOWARN;
1261 execution_never_reaches_here &= ~EXECSTATE_NOWARN;
1264 /* The post-"else" label */
1265 if (ln >= 0) assemble_forward_label_no(ln2);
1269 /* There was no "else". If we're unreachable, then the
1270 statement returned unconditionally, which means
1271 "if (1) return". Skip warnings. */
1272 if (!pre_unreach && execution_never_reaches_here) {
1273 execution_never_reaches_here |= EXECSTATE_NOWARN;
1279 /* -------------------------------------------------------------------- */
1280 /* inversion ---------------------------------------------------------- */
1281 /* -------------------------------------------------------------------- */
1283 case INVERSION_CODE:
1284 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
1285 INITAOT(&AO2, SHORT_CONSTANT_OT);
1288 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1289 assemblez_1(print_char_zc, temp_var1);
1291 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1292 assemblez_1(print_char_zc, temp_var1);
1294 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1295 assemblez_1(print_char_zc, temp_var1);
1297 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1298 assemblez_1(print_char_zc, temp_var1);
1301 /* -------------------------------------------------------------------- */
1302 /* jump <label> ------------------------------------------------------- */
1303 /* -------------------------------------------------------------------- */
1306 assemblez_jump(parse_label());
1309 /* -------------------------------------------------------------------- */
1310 /* move <expression> to <expression> ---------------------------------- */
1311 /* -------------------------------------------------------------------- */
1314 misc_keywords.enabled = TRUE;
1315 AO = parse_expression(QUANTITY_CONTEXT);
1318 misc_keywords.enabled = FALSE;
1319 if ((token_type != MISC_KEYWORD_TT)
1320 || (token_value != TO_MK))
1321 { ebf_error("'to'", token_text);
1322 panic_mode_error_recovery();
1326 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1327 QUANTITY_CONTEXT, -1);
1328 AO = code_generate(AO, QUANTITY_CONTEXT, -1);
1329 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
1330 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
1331 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
1332 { if (version_number >= 5)
1333 assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
1336 { assemblez_3_to(call_zc, veneer_routine(RT__ChT_VR),
1337 AO, AO2, temp_var1);
1341 assemblez_2(insert_obj_zc, AO, AO2);
1344 /* -------------------------------------------------------------------- */
1345 /* new_line ----------------------------------------------------------- */
1346 /* -------------------------------------------------------------------- */
1348 case NEW_LINE_CODE: assemblez_0(new_line_zc); break;
1350 /* -------------------------------------------------------------------- */
1351 /* objectloop (<initialisation>) <codeblock> -------------------------- */
1352 /* -------------------------------------------------------------------- */
1354 case OBJECTLOOP_CODE:
1356 match_open_bracket();
1358 INITAOT(&AO, VARIABLE_OT);
1359 if (token_type == LOCAL_VARIABLE_TT)
1360 AO.value = token_value;
1362 if ((token_type == SYMBOL_TT) &&
1363 (symbols[token_value].type == GLOBAL_VARIABLE_T))
1364 AO.value = symbols[token_value].value;
1366 { ebf_error("'objectloop' variable", token_text);
1367 panic_mode_error_recovery(); break;
1369 misc_keywords.enabled = TRUE;
1370 get_next_token(); flag = TRUE;
1371 misc_keywords.enabled = FALSE;
1372 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
1376 if ((token_type == MISC_KEYWORD_TT)
1377 && (token_value == NEAR_MK)) ln = 1;
1378 if ((token_type == MISC_KEYWORD_TT)
1379 && (token_value == FROM_MK)) ln = 2;
1380 if ((token_type == CND_TT) && (token_value == IN_COND))
1383 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
1390 { /* Old style (Inform 5) objectloops: note that we
1391 implement objectloop (a in b) in the old way since
1392 this runs through objects in a different order from
1393 the new way, and there may be existing Inform code
1395 assembly_operand AO4;
1398 sequence_point_follows = TRUE;
1399 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1400 QUANTITY_CONTEXT, -1);
1401 match_close_bracket();
1403 { INITAOTV(&AO3, VARIABLE_OT, 0);
1404 if (runtime_error_checking_switch)
1405 AO2 = check_nonzero_at_runtime(AO2, -1,
1407 assemblez_1_to(get_parent_zc, AO2, AO3);
1408 assemblez_objcode(get_child_zc, AO3, AO3, -2, TRUE);
1412 { INITAOTV(&AO3, VARIABLE_OT, 0);
1413 if (runtime_error_checking_switch)
1415 AO2 = check_nonzero_at_runtime(AO2, -1,
1418 assemblez_objcode(get_child_zc, AO2, AO3, -2, TRUE);
1421 assemblez_store(AO, AO2);
1422 assemblez_1_branch(jz_zc, AO, ln2 = next_label++, TRUE);
1423 assemble_label_no(ln4 = next_label++);
1424 parse_code_block(ln2, ln3 = next_label++, 0);
1425 sequence_point_follows = FALSE;
1426 assemble_label_no(ln3);
1427 if (runtime_error_checking_switch)
1428 { AO2 = check_nonzero_at_runtime(AO, ln2,
1431 && ((AO4.type != VARIABLE_OT)||(AO4.value != 0))
1432 && ((AO4.type != VARIABLE_OT)
1433 ||(AO4.value != AO.value)))
1434 { assembly_operand en_ao;
1435 INITAOTV(&en_ao, SHORT_CONSTANT_OT, OBJECTLOOP_BROKEN_RTE);
1436 assemblez_2_branch(jin_zc, AO, AO4,
1438 assemblez_3(call_vn_zc, veneer_routine(RT__Err_VR),
1440 assemblez_jump(ln2);
1441 assemble_label_no(next_label++);
1445 assemblez_objcode(get_sibling_zc, AO2, AO, ln4, TRUE);
1446 assemble_label_no(ln2);
1450 sequence_point_follows = TRUE;
1451 INITAOTV(&AO2, SHORT_CONSTANT_OT, 1);
1452 assemblez_store(AO, AO2);
1454 assemble_label_no(ln = next_label++);
1460 sequence_point_follows = TRUE;
1461 code_generate(parse_expression(CONDITION_CONTEXT),
1462 CONDITION_CONTEXT, ln3);
1463 match_close_bracket();
1465 parse_code_block(ln2, ln3, 0);
1467 sequence_point_follows = FALSE;
1468 assemble_label_no(ln3);
1470 INITAOTV(&AO2, LONG_CONSTANT_OT, no_objects);
1471 AO2.marker = NO_OBJS_MV;
1472 assemblez_2_branch(jg_zc, AO, AO2, ln2, TRUE);
1474 assemble_label_no(ln2);
1477 /* -------------------------------------------------------------------- */
1478 /* (see routine above) ------------------------------------------------ */
1479 /* -------------------------------------------------------------------- */
1483 parse_print_z(FALSE); return;
1484 case PRINT_RET_CODE:
1486 parse_print_z(TRUE); return;
1488 /* -------------------------------------------------------------------- */
1489 /* quit --------------------------------------------------------------- */
1490 /* -------------------------------------------------------------------- */
1492 case QUIT_CODE: assemblez_0(quit_zc); break;
1494 /* -------------------------------------------------------------------- */
1495 /* read <expression> <expression> [<Routine>] ------------------------- */
1496 /* -------------------------------------------------------------------- */
1499 INITAOTV(&AO, VARIABLE_OT, 252);
1501 code_generate(parse_expression(QUANTITY_CONTEXT),
1502 QUANTITY_CONTEXT, -1));
1503 if (version_number > 3)
1504 { INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
1505 INITAOTV(&AO4, SHORT_CONSTANT_OT, 0);
1506 assemblez_3(storeb_zc, AO, AO3, AO4);
1508 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1509 QUANTITY_CONTEXT, -1);
1512 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
1515 { if (version_number == 3)
1517 "In Version 3 no status-line drawing routine can be given");
1519 { assembly_operand AO5;
1520 /* Move the temp4 (buffer) value to the stack,
1521 since the routine might alter temp4. */
1522 assemblez_store(stack_pointer, AO);
1525 AO5 = parse_expression(CONSTANT_CONTEXT);
1527 if (version_number >= 5)
1528 assemblez_1(call_1n_zc, AO5);
1530 assemblez_1_to(call_zc, AO5, temp_var1);
1534 if (version_number > 4)
1535 { assemblez_2_to(aread_zc, AO, AO2, temp_var1);
1537 else assemblez_2(sread_zc, AO, AO2);
1540 /* -------------------------------------------------------------------- */
1541 /* remove <expression> ------------------------------------------------ */
1542 /* -------------------------------------------------------------------- */
1545 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1546 QUANTITY_CONTEXT, -1);
1547 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
1548 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
1549 { if (version_number >= 5)
1550 assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR),
1553 { assemblez_2_to(call_zc, veneer_routine(RT__ChR_VR),
1558 assemblez_1(remove_obj_zc, AO);
1561 /* -------------------------------------------------------------------- */
1562 /* restore <label> ---------------------------------------------------- */
1563 /* -------------------------------------------------------------------- */
1566 if (version_number < 5)
1567 assemblez_0_branch(restore_zc, parse_label(), TRUE);
1569 { INITAOTV(&AO2, SHORT_CONSTANT_OT, 2);
1570 assemblez_0_to(restore_zc, temp_var1);
1571 assemblez_2_branch(je_zc, temp_var1, AO2, parse_label(), TRUE);
1575 /* -------------------------------------------------------------------- */
1576 /* return [<expression>] ---------------------------------------------- */
1577 /* -------------------------------------------------------------------- */
1581 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
1582 { assemblez_0(rtrue_zc); return; }
1584 AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
1585 QUANTITY_CONTEXT, -1);
1586 if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 0)
1587 && (AO.marker == 0))
1588 { assemblez_0(rfalse_zc); break; }
1589 if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 1)
1590 && (AO.marker == 0))
1591 { assemblez_0(rtrue_zc); break; }
1592 if ((AO.type == VARIABLE_OT) && (AO.value == 0))
1593 { assemblez_0(ret_popped_zc); break; }
1594 assemblez_1(ret_zc, AO);
1597 /* -------------------------------------------------------------------- */
1598 /* rfalse ------------------------------------------------------------- */
1599 /* -------------------------------------------------------------------- */
1601 case RFALSE_CODE: assemblez_0(rfalse_zc); break;
1603 /* -------------------------------------------------------------------- */
1604 /* rtrue -------------------------------------------------------------- */
1605 /* -------------------------------------------------------------------- */
1607 case RTRUE_CODE: assemblez_0(rtrue_zc); break;
1609 /* -------------------------------------------------------------------- */
1610 /* save <label> ------------------------------------------------------- */
1611 /* -------------------------------------------------------------------- */
1614 if (version_number < 5)
1615 assemblez_0_branch(save_zc, parse_label(), TRUE);
1617 { INITAOTV(&AO, VARIABLE_OT, 255);
1618 assemblez_0_to(save_zc, AO);
1619 assemblez_1_branch(jz_zc, AO, parse_label(), FALSE);
1623 /* -------------------------------------------------------------------- */
1624 /* spaces <expression> ------------------------------------------------ */
1625 /* -------------------------------------------------------------------- */
1628 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1629 QUANTITY_CONTEXT, -1);
1630 INITAOTV(&AO2, VARIABLE_OT, 255);
1632 assemblez_store(AO2, AO);
1634 INITAOTV(&AO, SHORT_CONSTANT_OT, 32);
1635 INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
1637 assemblez_2_branch(jl_zc, AO2, AO3, ln = next_label++, TRUE);
1638 assemble_label_no(ln2 = next_label++);
1639 assemblez_1(print_char_zc, AO);
1641 assemblez_1_branch(jz_zc, AO2, ln2, FALSE);
1642 assemble_label_no(ln);
1645 /* -------------------------------------------------------------------- */
1646 /* string <expression> <literal-string> ------------------------------- */
1647 /* -------------------------------------------------------------------- */
1650 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
1651 INITAOTV(&AO2, SHORT_CONSTANT_OT, 12);
1652 INITAOTV(&AO3, VARIABLE_OT, 252);
1653 assemblez_2_to(loadw_zc, AO, AO2, AO3);
1654 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1655 QUANTITY_CONTEXT, -1);
1656 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
1657 /* Compile-time check */
1658 if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
1659 error_max_dynamic_strings(AO2.value);
1664 if (token_type == DQ_TT)
1665 { INITAOT(&AO4, LONG_CONSTANT_OT);
1666 /* This string must be in low memory so that the
1667 dynamic string table can refer to it. */
1668 AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
1672 AO4 = parse_expression(CONSTANT_CONTEXT);
1674 assemblez_3(storew_zc, AO3, AO2, AO4);
1677 /* -------------------------------------------------------------------- */
1678 /* style roman/reverse/bold/underline/fixed --------------------------- */
1679 /* -------------------------------------------------------------------- */
1682 if (version_number==3)
1684 "The 'style' statement cannot be used for Version 3 games");
1685 panic_mode_error_recovery();
1689 misc_keywords.enabled = TRUE;
1691 misc_keywords.enabled = FALSE;
1692 if ((token_type != MISC_KEYWORD_TT)
1693 || ((token_value != ROMAN_MK)
1694 && (token_value != REVERSE_MK)
1695 && (token_value != BOLD_MK)
1696 && (token_value != UNDERLINE_MK)
1697 && (token_value != FIXED_MK)))
1699 "'roman', 'bold', 'underline', 'reverse' or 'fixed'",
1701 panic_mode_error_recovery();
1705 INITAOT(&AO, SHORT_CONSTANT_OT);
1707 { case ROMAN_MK: AO.value = 0; break;
1708 case REVERSE_MK: AO.value = 1; break;
1709 case BOLD_MK: AO.value = 2; break;
1710 case UNDERLINE_MK: AO.value = 4; break;
1711 case FIXED_MK: AO.value = 8; break;
1713 assemblez_1(set_text_style_zc, AO); break;
1715 /* -------------------------------------------------------------------- */
1716 /* switch (<expression>) <codeblock> ---------------------------------- */
1717 /* -------------------------------------------------------------------- */
1720 match_open_bracket();
1721 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1722 QUANTITY_CONTEXT, -1);
1723 match_close_bracket();
1725 INITAOTV(&AO2, VARIABLE_OT, 255);
1726 assemblez_store(AO2, AO);
1728 parse_code_block(ln = next_label++, continue_label, 1);
1729 assemble_forward_label_no(ln);
1732 /* -------------------------------------------------------------------- */
1733 /* while (<condition>) <codeblock> ------------------------------------ */
1734 /* -------------------------------------------------------------------- */
1737 assemble_label_no(ln = next_label++);
1738 match_open_bracket();
1740 code_generate(parse_expression(CONDITION_CONTEXT),
1741 CONDITION_CONTEXT, ln2 = next_label++);
1742 match_close_bracket();
1744 parse_code_block(ln2, ln, 0);
1745 sequence_point_follows = FALSE;
1747 assemble_forward_label_no(ln2);
1750 /* -------------------------------------------------------------------- */
1753 error("'default' without matching 'switch'"); break;
1755 error("'else' without matching 'if'"); break;
1757 error("'until' without matching 'do'");
1758 panic_mode_error_recovery(); return;
1761 StatementTerminator:
1764 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
1765 { ebf_error("';'", token_text);
1770 static void parse_statement_g(int break_label, int continue_label)
1771 { int ln, ln2, ln3, ln4, flag, onstack;
1772 int pre_unreach, labelexists;
1773 assembly_operand AO, AO2, AO3, AO4;
1774 debug_location spare_debug_location1, spare_debug_location2;
1778 if ((token_type == SEP_TT) && (token_value == HASH_SEP))
1779 { parse_directive(TRUE);
1780 parse_statement(break_label, continue_label); return;
1783 if ((token_type == SEP_TT) && (token_value == AT_SEP))
1784 { parse_assembly(); return;
1787 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
1789 if (token_type == DQ_TT)
1790 { parse_print_g(TRUE); return;
1793 if ((token_type == SEP_TT) && (token_value == LESS_SEP))
1794 { parse_action(); goto StatementTerminator; }
1796 if (token_type == EOF_TT)
1797 { ebf_error("statement", token_text); return; }
1799 if (token_type != STATEMENT_TT)
1801 AO = parse_expression(VOID_CONTEXT);
1802 code_generate(AO, VOID_CONTEXT, -1);
1803 if (vivc_flag) { panic_mode_error_recovery(); return; }
1804 goto StatementTerminator;
1807 statements.enabled = FALSE;
1812 /* -------------------------------------------------------------------- */
1813 /* box <string-1> ... <string-n> -------------------------------------- */
1814 /* -------------------------------------------------------------------- */
1817 INITAOT(&AO3, CONSTANT_OT);
1818 AO3.value = begin_table_array();
1819 AO3.marker = ARRAY_MV;
1823 if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
1825 if (token_type != DQ_TT)
1826 ebf_error("text of box line in double-quotes",
1829 for (i=0, j=0; token_text[i] != 0; j++)
1830 if (token_text[i] == '@')
1831 { if (token_text[i+1] == '@')
1833 while (isdigit(token_text[i])) i++;
1837 if (token_text[i] != 0) i++;
1838 if (token_text[i] != 0) i++;
1842 if (j > ln2) ln2 = j;
1845 array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
1847 finish_array(ln, FALSE);
1849 error("No lines of text given for 'box' display");
1852 AO2.value = ln2; set_constant_ot(&AO2);
1853 assembleg_call_2(veneer_routine(Box__Routine_VR),
1854 AO2, AO3, zero_operand);
1857 /* -------------------------------------------------------------------- */
1858 /* break -------------------------------------------------------------- */
1859 /* -------------------------------------------------------------------- */
1862 if (break_label == -1)
1863 error("'break' can only be used in a loop or 'switch' block");
1865 assembleg_jump(break_label);
1868 /* -------------------------------------------------------------------- */
1869 /* continue ----------------------------------------------------------- */
1870 /* -------------------------------------------------------------------- */
1873 if (continue_label == -1)
1874 error("'continue' can only be used in a loop block");
1876 assembleg_jump(continue_label);
1879 /* -------------------------------------------------------------------- */
1880 /* do <codeblock> until (<condition>) --------------------------------- */
1881 /* -------------------------------------------------------------------- */
1884 assemble_label_no(ln = next_label++);
1885 ln2 = next_label++; ln3 = next_label++;
1886 parse_code_block(ln3, ln2, 0);
1887 statements.enabled = TRUE;
1889 if ((token_type == STATEMENT_TT)
1890 && (token_value == UNTIL_CODE))
1891 { assemble_forward_label_no(ln2);
1892 match_open_bracket();
1893 AO = parse_expression(CONDITION_CONTEXT);
1894 match_close_bracket();
1895 code_generate(AO, CONDITION_CONTEXT, ln);
1897 else error("'do' without matching 'until'");
1899 assemble_forward_label_no(ln3);
1902 /* -------------------------------------------------------------------- */
1903 /* font on/off -------------------------------------------------------- */
1904 /* -------------------------------------------------------------------- */
1907 misc_keywords.enabled = TRUE;
1909 misc_keywords.enabled = FALSE;
1910 if ((token_type != MISC_KEYWORD_TT)
1911 || ((token_value != ON_MK)
1912 && (token_value != OFF_MK)))
1913 { ebf_error("'on' or 'off'", token_text);
1914 panic_mode_error_recovery();
1918 /* Call glk_set_style(normal or preformatted) */
1921 set_constant_ot(&AO);
1922 if (token_value == ON_MK)
1926 assembleg_call_2(veneer_routine(Glk__Wrap_VR),
1927 AO, AO2, zero_operand);
1930 /* -------------------------------------------------------------------- */
1931 /* for (<initialisation> : <continue-condition> : <updating>) --------- */
1932 /* -------------------------------------------------------------------- */
1934 /* Note that it's legal for any or all of the three sections of a
1935 'for' specification to be empty. This 'for' implementation
1936 often wastes 3 bytes with a redundant branch rather than keep
1937 expression parse trees for long periods (as previous versions
1938 of Inform did, somewhat crudely by simply storing the textual
1939 form of a 'for' loop). It is adequate for now. */
1942 match_open_bracket();
1945 /* Initialisation code */
1946 AO.type = OMITTED_OT;
1947 spare_debug_location1 = statement_debug_location;
1948 AO2.type = OMITTED_OT; flag = 0;
1949 spare_debug_location2 = statement_debug_location;
1951 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1953 if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
1954 { sequence_point_follows = TRUE;
1955 statement_debug_location = get_token_location();
1956 code_generate(parse_expression(FORINIT_CONTEXT),
1960 if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
1962 if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
1963 { assemble_label_no(ln = next_label++);
1965 parse_code_block(ln2, ln, 0);
1966 sequence_point_follows = FALSE;
1967 if (!execution_never_reaches_here)
1969 assemble_forward_label_no(ln2);
1975 if (!match_colon()) break;
1979 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1981 spare_debug_location1 = get_token_location();
1982 AO = parse_expression(CONDITION_CONTEXT);
1983 if (!match_colon()) break;
1988 if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
1990 spare_debug_location2 = get_token_location();
1991 AO2 = parse_expression(VOID_CONTEXT);
1992 match_close_bracket();
1993 flag = test_for_incdec(AO2);
2000 if ((AO2.type == OMITTED_OT) || (flag != 0))
2002 assemble_label_no(ln);
2003 if (flag==0) assemble_label_no(ln2);
2005 /* The "finished yet?" condition */
2007 if (AO.type != OMITTED_OT)
2008 { sequence_point_follows = TRUE;
2009 statement_debug_location = spare_debug_location1;
2010 code_generate(AO, CONDITION_CONTEXT, ln3);
2016 /* This is the jump which could be avoided with the aid
2017 of long-term expression storage */
2019 sequence_point_follows = FALSE;
2020 assembleg_jump(ln2);
2022 /* The "update" part */
2024 assemble_label_no(ln);
2025 sequence_point_follows = TRUE;
2026 statement_debug_location = spare_debug_location2;
2027 code_generate(AO2, VOID_CONTEXT, -1);
2029 assemble_label_no(ln2);
2031 /* The "finished yet?" condition */
2033 if (AO.type != OMITTED_OT)
2034 { sequence_point_follows = TRUE;
2035 statement_debug_location = spare_debug_location1;
2036 code_generate(AO, CONDITION_CONTEXT, ln3);
2042 /* In this optimised case, update code is at the end
2043 of the loop block, so "continue" goes there */
2045 parse_code_block(ln3, ln2, 0);
2046 assemble_label_no(ln2);
2048 sequence_point_follows = TRUE;
2049 statement_debug_location = spare_debug_location2;
2053 if (AO3.value >= MAX_LOCAL_VARIABLES)
2054 AO3.type = GLOBALVAR_OT;
2056 AO3.type = LOCALVAR_OT;
2057 assembleg_3(add_gc, AO3, one_operand, AO3);
2062 if (AO3.value >= MAX_LOCAL_VARIABLES)
2063 AO3.type = GLOBALVAR_OT;
2065 AO3.type = LOCALVAR_OT;
2066 assembleg_3(sub_gc, AO3, one_operand, AO3);
2072 /* In the unoptimised case, update code is at the
2073 start of the loop block, so "continue" goes there */
2075 parse_code_block(ln3, ln, 0);
2076 if (!execution_never_reaches_here)
2077 { sequence_point_follows = FALSE;
2082 assemble_forward_label_no(ln3);
2085 /* -------------------------------------------------------------------- */
2086 /* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
2087 /* -------------------------------------------------------------------- */
2090 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2091 QUANTITY_CONTEXT, -1);
2092 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
2093 if ((AO.type == LOCALVAR_OT) && (AO.value == 0))
2100 if ((token_type == SEP_TT)
2101 && (token_value == SEMICOLON_SEP)) {
2103 assembleg_2(copy_gc, stack_pointer, zero_operand);
2107 if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
2113 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2114 QUANTITY_CONTEXT, -1);
2115 check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
2116 if (runtime_error_checking_switch && (!veneer_mode))
2117 { ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR);
2118 if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
2119 /* already on stack */
2122 assembleg_store(stack_pointer, AO2);
2125 assembleg_2(stkpeek_gc, one_operand, stack_pointer);
2127 assembleg_store(stack_pointer, AO);
2128 assembleg_3(call_gc, veneer_routine(ln2), two_operand,
2132 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
2134 set_constant_ot(&AO2);
2137 INITAOTV(&AO3, BYTECONSTANT_OT, 8);
2138 assembleg_3(add_gc, AO2, AO3, stack_pointer);
2139 AO2 = stack_pointer;
2142 if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0))
2143 assembleg_2(stkpeek_gc, one_operand,
2146 assembleg_2(stkpeek_gc, zero_operand,
2153 assembleg_3(astorebit_gc, AO, AO2, AO3);
2157 /* -------------------------------------------------------------------- */
2158 /* if (<condition>) <codeblock> [else <codeblock>] -------------------- */
2159 /* -------------------------------------------------------------------- */
2162 flag = FALSE; /* set if there's an "else" */
2164 pre_unreach = execution_never_reaches_here;
2166 match_open_bracket();
2167 AO = parse_expression(CONDITION_CONTEXT);
2168 match_close_bracket();
2170 statements.enabled = TRUE;
2172 if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
2175 if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
2183 code_generate(AO, CONDITION_CONTEXT, ln);
2185 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
2186 /* If the condition never falls through to here, then
2187 it was an "if (0)" test. Our convention is to skip
2188 the "not reached" warnings for this case. */
2189 execution_never_reaches_here |= EXECSTATE_NOWARN;
2192 /* The "if" block */
2193 if (ln >= 0) parse_code_block(break_label, continue_label, 0);
2196 if ((token_type != SEP_TT)
2197 || (token_value != SEMICOLON_SEP))
2198 { ebf_error("';'", token_text);
2203 statements.enabled = TRUE;
2206 /* An #if directive around the ELSE clause is legal. */
2207 while ((token_type == SEP_TT) && (token_value == HASH_SEP))
2208 { parse_directive(TRUE);
2209 statements.enabled = TRUE;
2213 if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
2216 { ln2 = next_label++;
2217 if (!execution_never_reaches_here)
2218 { sequence_point_follows = FALSE;
2219 assembleg_jump(ln2);
2223 else put_token_back();
2225 /* The "else" label (or end of statement, if there is no "else") */
2226 labelexists = FALSE;
2227 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
2231 /* If labelexists is false, then we started with
2232 "if (1)". In this case, we don't want a "not
2233 reached" warning on the "else" block. We
2234 temporarily disable the NOWARN flag, and restore it
2236 int saved_unreach = 0;
2237 if (execution_never_reaches_here && !labelexists) {
2238 saved_unreach = execution_never_reaches_here;
2239 execution_never_reaches_here |= EXECSTATE_NOWARN;
2242 /* The "else" block */
2243 parse_code_block(break_label, continue_label, 0);
2245 if (execution_never_reaches_here && !labelexists) {
2246 if (saved_unreach & EXECSTATE_NOWARN)
2247 execution_never_reaches_here |= EXECSTATE_NOWARN;
2249 execution_never_reaches_here &= ~EXECSTATE_NOWARN;
2252 /* The post-"else" label */
2253 if (ln >= 0) assemble_forward_label_no(ln2);
2257 /* There was no "else". If we're unreachable, then the
2258 statement returned unconditionally, which means
2259 "if (1) return". Skip warnings. */
2260 if (!pre_unreach && execution_never_reaches_here) {
2261 execution_never_reaches_here |= EXECSTATE_NOWARN;
2267 /* -------------------------------------------------------------------- */
2268 /* inversion ---------------------------------------------------------- */
2269 /* -------------------------------------------------------------------- */
2271 case INVERSION_CODE:
2272 INITAOTV(&AO2, DEREFERENCE_OT, GLULX_HEADER_SIZE+8);
2273 assembleg_2(copyb_gc, AO2, stack_pointer);
2274 assembleg_1(streamchar_gc, stack_pointer);
2275 AO2.value = GLULX_HEADER_SIZE+9;
2276 assembleg_2(copyb_gc, AO2, stack_pointer);
2277 assembleg_1(streamchar_gc, stack_pointer);
2278 AO2.value = GLULX_HEADER_SIZE+10;
2279 assembleg_2(copyb_gc, AO2, stack_pointer);
2280 assembleg_1(streamchar_gc, stack_pointer);
2281 AO2.value = GLULX_HEADER_SIZE+11;
2282 assembleg_2(copyb_gc, AO2, stack_pointer);
2283 assembleg_1(streamchar_gc, stack_pointer);
2285 if (/* DISABLES CODE */ (0)) {
2288 set_constant_ot(&AO);
2289 assembleg_1(streamchar_gc, AO);
2291 set_constant_ot(&AO);
2292 assembleg_1(streamchar_gc, AO);
2294 AO2.value = GLULX_HEADER_SIZE+12;
2295 assembleg_2(copyb_gc, AO2, stack_pointer);
2296 assembleg_1(streamchar_gc, stack_pointer);
2297 AO2.value = GLULX_HEADER_SIZE+13;
2298 assembleg_2(copyb_gc, AO2, stack_pointer);
2299 assembleg_1(streamchar_gc, stack_pointer);
2300 AO2.value = GLULX_HEADER_SIZE+14;
2301 assembleg_2(copyb_gc, AO2, stack_pointer);
2302 assembleg_1(streamchar_gc, stack_pointer);
2303 AO2.value = GLULX_HEADER_SIZE+15;
2304 assembleg_2(copyb_gc, AO2, stack_pointer);
2305 assembleg_1(streamchar_gc, stack_pointer);
2309 set_constant_ot(&AO);
2310 assembleg_1(streamchar_gc, AO);
2315 /* -------------------------------------------------------------------- */
2316 /* jump <label> ------------------------------------------------------- */
2317 /* -------------------------------------------------------------------- */
2320 assembleg_jump(parse_label());
2323 /* -------------------------------------------------------------------- */
2324 /* move <expression> to <expression> ---------------------------------- */
2325 /* -------------------------------------------------------------------- */
2328 misc_keywords.enabled = TRUE;
2329 AO = parse_expression(QUANTITY_CONTEXT);
2332 misc_keywords.enabled = FALSE;
2333 if ((token_type != MISC_KEYWORD_TT)
2334 || (token_value != TO_MK))
2335 { ebf_error("'to'", token_text);
2336 panic_mode_error_recovery();
2340 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2341 QUANTITY_CONTEXT, -1);
2342 AO = code_generate(AO, QUANTITY_CONTEXT, -1);
2343 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
2344 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
2345 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
2346 assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2,
2349 assembleg_call_2(veneer_routine(OB__Move_VR), AO, AO2,
2353 /* -------------------------------------------------------------------- */
2354 /* new_line ----------------------------------------------------------- */
2355 /* -------------------------------------------------------------------- */
2358 INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
2359 assembleg_1(streamchar_gc, AO);
2362 /* -------------------------------------------------------------------- */
2363 /* objectloop (<initialisation>) <codeblock> -------------------------- */
2364 /* -------------------------------------------------------------------- */
2366 case OBJECTLOOP_CODE:
2368 match_open_bracket();
2370 if (token_type == LOCAL_VARIABLE_TT) {
2371 INITAOTV(&AO, LOCALVAR_OT, token_value);
2373 else if ((token_type == SYMBOL_TT) &&
2374 (symbols[token_value].type == GLOBAL_VARIABLE_T)) {
2375 INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
2378 ebf_error("'objectloop' variable", token_text);
2379 panic_mode_error_recovery();
2382 misc_keywords.enabled = TRUE;
2383 get_next_token(); flag = TRUE;
2384 misc_keywords.enabled = FALSE;
2385 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
2389 if ((token_type == MISC_KEYWORD_TT)
2390 && (token_value == NEAR_MK)) ln = 1;
2391 if ((token_type == MISC_KEYWORD_TT)
2392 && (token_value == FROM_MK)) ln = 2;
2393 if ((token_type == CND_TT) && (token_value == IN_COND))
2396 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
2403 /* Old style (Inform 5) objectloops: note that we
2404 implement objectloop (a in b) in the old way since
2405 this runs through objects in a different order from
2406 the new way, and there may be existing Inform code
2408 assembly_operand AO4, AO5;
2411 sequence_point_follows = TRUE;
2412 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2413 QUANTITY_CONTEXT, -1);
2414 match_close_bracket();
2416 if (runtime_error_checking_switch)
2417 AO2 = check_nonzero_at_runtime(AO2, -1,
2419 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
2420 assembleg_3(aload_gc, AO2, AO4, stack_pointer);
2421 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
2422 assembleg_3(aload_gc, stack_pointer, AO4, stack_pointer);
2423 AO2 = stack_pointer;
2426 if (runtime_error_checking_switch) {
2428 AO2 = check_nonzero_at_runtime(AO2, -1,
2431 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
2432 assembleg_3(aload_gc, AO2, AO4, stack_pointer);
2433 AO2 = stack_pointer;
2438 assembleg_store(AO, AO2);
2439 assembleg_1_branch(jz_gc, AO, ln2 = next_label++);
2440 assemble_label_no(ln4 = next_label++);
2441 parse_code_block(ln2, ln3 = next_label++, 0);
2442 sequence_point_follows = FALSE;
2443 assemble_label_no(ln3);
2444 if (runtime_error_checking_switch) {
2445 AO2 = check_nonzero_at_runtime(AO, ln2,
2448 && ((AO5.type != LOCALVAR_OT)||(AO5.value != 0))
2449 && ((AO5.type != LOCALVAR_OT)||(AO5.value != AO.value)))
2450 { assembly_operand en_ao;
2452 en_ao.value = OBJECTLOOP_BROKEN_RTE;
2453 set_constant_ot(&en_ao);
2454 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
2455 assembleg_3(aload_gc, AO, AO4, stack_pointer);
2456 assembleg_2_branch(jeq_gc, stack_pointer, AO5,
2458 assembleg_call_2(veneer_routine(RT__Err_VR),
2459 en_ao, AO, zero_operand);
2460 assembleg_jump(ln2);
2461 assemble_label_no(next_label++);
2467 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_SIBLING());
2468 assembleg_3(aload_gc, AO2, AO4, AO);
2469 assembleg_1_branch(jnz_gc, AO, ln4);
2470 assemble_label_no(ln2);
2474 sequence_point_follows = TRUE;
2475 ln = symbol_index("Class", -1);
2476 INITAOT(&AO2, CONSTANT_OT);
2477 AO2.value = symbols[ln].value;
2478 AO2.marker = OBJECT_MV;
2479 assembleg_store(AO, AO2);
2481 assemble_label_no(ln = next_label++);
2487 sequence_point_follows = TRUE;
2488 code_generate(parse_expression(CONDITION_CONTEXT),
2489 CONDITION_CONTEXT, ln3);
2490 match_close_bracket();
2492 parse_code_block(ln2, ln3, 0);
2494 sequence_point_follows = FALSE;
2495 assemble_label_no(ln3);
2496 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHAIN());
2497 assembleg_3(aload_gc, AO, AO4, AO);
2498 assembleg_1_branch(jnz_gc, AO, ln);
2499 assemble_label_no(ln2);
2502 /* -------------------------------------------------------------------- */
2503 /* (see routine above) ------------------------------------------------ */
2504 /* -------------------------------------------------------------------- */
2508 parse_print_g(FALSE); return;
2509 case PRINT_RET_CODE:
2511 parse_print_g(TRUE); return;
2513 /* -------------------------------------------------------------------- */
2514 /* quit --------------------------------------------------------------- */
2515 /* -------------------------------------------------------------------- */
2518 assembleg_0(quit_gc); break;
2520 /* -------------------------------------------------------------------- */
2521 /* remove <expression> ------------------------------------------------ */
2522 /* -------------------------------------------------------------------- */
2525 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2526 QUANTITY_CONTEXT, -1);
2527 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
2528 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
2529 assembleg_call_1(veneer_routine(RT__ChR_VR), AO,
2532 assembleg_call_1(veneer_routine(OB__Remove_VR), AO,
2536 /* -------------------------------------------------------------------- */
2537 /* return [<expression>] ---------------------------------------------- */
2538 /* -------------------------------------------------------------------- */
2542 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) {
2543 assembleg_1(return_gc, one_operand);
2547 AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
2548 QUANTITY_CONTEXT, -1);
2549 assembleg_1(return_gc, AO);
2552 /* -------------------------------------------------------------------- */
2553 /* rfalse ------------------------------------------------------------- */
2554 /* -------------------------------------------------------------------- */
2557 assembleg_1(return_gc, zero_operand);
2560 /* -------------------------------------------------------------------- */
2561 /* rtrue -------------------------------------------------------------- */
2562 /* -------------------------------------------------------------------- */
2565 assembleg_1(return_gc, one_operand);
2568 /* -------------------------------------------------------------------- */
2569 /* spaces <expression> ------------------------------------------------ */
2570 /* -------------------------------------------------------------------- */
2573 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2574 QUANTITY_CONTEXT, -1);
2576 assembleg_store(temp_var1, AO);
2579 AO.value = 32; set_constant_ot(&AO);
2581 assembleg_2_branch(jlt_gc, temp_var1, one_operand,
2583 assemble_label_no(ln2 = next_label++);
2584 assembleg_1(streamchar_gc, AO);
2585 assembleg_dec(temp_var1);
2586 assembleg_1_branch(jnz_gc, temp_var1, ln2);
2587 assemble_label_no(ln);
2590 /* -------------------------------------------------------------------- */
2591 /* string <expression> <literal-string> ------------------------------- */
2592 /* -------------------------------------------------------------------- */
2595 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2596 QUANTITY_CONTEXT, -1);
2597 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
2598 /* Compile-time check */
2599 if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
2600 error_max_dynamic_strings(AO2.value);
2604 if (token_type == DQ_TT)
2605 { INITAOT(&AO4, CONSTANT_OT);
2606 /* This is not actually placed in low memory; Glulx
2607 has no such concept. We use the LOWSTRING flag
2608 for compatibility with older compiler behavior. */
2609 AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
2610 AO4.marker = STRING_MV;
2614 AO4 = parse_expression(CONSTANT_CONTEXT);
2616 assembleg_call_2(veneer_routine(Dynam__String_VR),
2617 AO2, AO4, zero_operand);
2620 /* -------------------------------------------------------------------- */
2621 /* style roman/reverse/bold/underline/fixed --------------------------- */
2622 /* -------------------------------------------------------------------- */
2625 misc_keywords.enabled = TRUE;
2627 misc_keywords.enabled = FALSE;
2628 if ((token_type != MISC_KEYWORD_TT)
2629 || ((token_value != ROMAN_MK)
2630 && (token_value != REVERSE_MK)
2631 && (token_value != BOLD_MK)
2632 && (token_value != UNDERLINE_MK)
2633 && (token_value != FIXED_MK)))
2635 "'roman', 'bold', 'underline', 'reverse' or 'fixed'",
2637 panic_mode_error_recovery();
2641 /* Call glk_set_style() */
2645 set_constant_ot(&AO);
2649 AO2 = zero_operand; /* normal */
2653 AO2.value = 5; /* alert */
2654 set_constant_ot(&AO2);
2658 AO2.value = 4; /* subheader */
2659 set_constant_ot(&AO2);
2662 AO2 = one_operand; /* emphasized */
2665 AO2 = two_operand; /* preformatted */
2668 assembleg_call_2(veneer_routine(Glk__Wrap_VR),
2669 AO, AO2, zero_operand);
2672 /* -------------------------------------------------------------------- */
2673 /* switch (<expression>) <codeblock> ---------------------------------- */
2674 /* -------------------------------------------------------------------- */
2677 match_open_bracket();
2678 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2679 QUANTITY_CONTEXT, -1);
2680 match_close_bracket();
2682 assembleg_store(temp_var1, AO);
2684 parse_code_block(ln = next_label++, continue_label, 1);
2685 assemble_forward_label_no(ln);
2688 /* -------------------------------------------------------------------- */
2689 /* while (<condition>) <codeblock> ------------------------------------ */
2690 /* -------------------------------------------------------------------- */
2693 assemble_label_no(ln = next_label++);
2694 match_open_bracket();
2696 code_generate(parse_expression(CONDITION_CONTEXT),
2697 CONDITION_CONTEXT, ln2 = next_label++);
2698 match_close_bracket();
2700 parse_code_block(ln2, ln, 0);
2701 sequence_point_follows = FALSE;
2703 assemble_forward_label_no(ln2);
2706 /* -------------------------------------------------------------------- */
2709 error("'default' without matching 'switch'"); break;
2711 error("'else' without matching 'if'"); break;
2713 error("'until' without matching 'do'");
2714 panic_mode_error_recovery(); return;
2716 /* -------------------------------------------------------------------- */
2718 /* And a useful default, which will never be triggered in a complete
2719 Inform compiler, but which is important in development. */
2722 error("*** Statement code gen: Can't generate yet ***\n");
2723 panic_mode_error_recovery(); return;
2726 StatementTerminator:
2729 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
2730 { ebf_error("';'", token_text);
2735 extern void parse_statement(int break_label, int continue_label)
2738 int saved_entire_flag;
2740 res = parse_named_label_statements();
2744 saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
2745 if (execution_never_reaches_here)
2746 execution_never_reaches_here |= EXECSTATE_ENTIRE;
2749 parse_statement_z(break_label, continue_label);
2751 parse_statement_g(break_label, continue_label);
2753 if (saved_entire_flag)
2754 execution_never_reaches_here |= EXECSTATE_ENTIRE;
2756 execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
2759 /* ========================================================================= */
2760 /* Data structure management routines */
2761 /* ------------------------------------------------------------------------- */
2763 extern void init_states_vars(void)
2767 extern void states_begin_pass(void)
2771 extern void states_allocate_arrays(void)
2775 extern void states_free_arrays(void)
2779 /* ========================================================================= */