1 /* ------------------------------------------------------------------------- */
2 /* "states" : Statement translator */
4 /* Part of Inform 6.40 */
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);
1104 && (flag>=MAX_LOCAL_VARIABLES) && (flag<LOWEST_SYSTEM_VAR_NUMBER))
1105 AO3.marker = VARIABLE_MV;
1106 assemblez_1(inc_zc, AO3);
1109 { INITAOTV(&AO3, SHORT_CONSTANT_OT, -flag);
1110 if ((module_switch) && (flag>=MAX_LOCAL_VARIABLES)
1111 && (flag<LOWEST_SYSTEM_VAR_NUMBER))
1112 AO3.marker = VARIABLE_MV;
1113 assemblez_1(dec_zc, AO3);
1119 /* In the unoptimised case, update code is at the
1120 start of the loop block, so "continue" goes there */
1122 parse_code_block(ln3, ln, 0);
1123 if (!execution_never_reaches_here)
1124 { sequence_point_follows = FALSE;
1129 assemble_forward_label_no(ln3);
1132 /* -------------------------------------------------------------------- */
1133 /* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
1134 /* -------------------------------------------------------------------- */
1137 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1138 QUANTITY_CONTEXT, -1);
1139 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
1140 if ((AO.type == VARIABLE_OT) && (AO.value == 0))
1141 { INITAOTV(&AO, SHORT_CONSTANT_OT, 252);
1142 if (version_number != 6) assemblez_1(pull_zc, AO);
1143 else assemblez_0_to(pull_zc, AO);
1144 AO.type = VARIABLE_OT;
1149 if ((token_type == SEP_TT)&&(token_value == SEMICOLON_SEP))
1151 if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
1157 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1158 QUANTITY_CONTEXT, -1);
1159 check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
1160 if (runtime_error_checking_switch)
1161 { ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
1162 if (version_number >= 5)
1163 assemblez_3(call_vn_zc, veneer_routine(ln2),
1167 assemblez_3_to(call_zc, veneer_routine(ln2),
1168 AO, AO2, temp_var1);
1172 assemblez_2(ln, AO, AO2);
1175 /* -------------------------------------------------------------------- */
1176 /* if (<condition>) <codeblock> [else <codeblock>] -------------------- */
1177 /* -------------------------------------------------------------------- */
1180 flag = FALSE; /* set if there's an "else" */
1182 pre_unreach = execution_never_reaches_here;
1184 match_open_bracket();
1185 AO = parse_expression(CONDITION_CONTEXT);
1186 match_close_bracket();
1188 statements.enabled = TRUE;
1190 if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
1193 if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
1201 code_generate(AO, CONDITION_CONTEXT, ln);
1203 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
1204 /* If the condition never falls through to here, then
1205 it was an "if (0)" test. Our convention is to skip
1206 the "not reached" warnings for this case. */
1207 execution_never_reaches_here |= EXECSTATE_NOWARN;
1210 /* The "if" block */
1211 if (ln >= 0) parse_code_block(break_label, continue_label, 0);
1214 if ((token_type != SEP_TT)
1215 || (token_value != SEMICOLON_SEP))
1216 { ebf_error("';'", token_text);
1221 statements.enabled = TRUE;
1224 /* An #if directive around the ELSE clause is legal. */
1225 while ((token_type == SEP_TT) && (token_value == HASH_SEP))
1226 { parse_directive(TRUE);
1227 statements.enabled = TRUE;
1231 if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
1234 { ln2 = next_label++;
1235 if (!execution_never_reaches_here)
1236 { sequence_point_follows = FALSE;
1237 assemblez_jump(ln2);
1241 else put_token_back();
1243 /* The "else" label (or end of statement, if there is no "else") */
1244 labelexists = FALSE;
1245 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
1249 /* If labelexists is false, then we started with
1250 "if (1)". In this case, we don't want a "not
1251 reached" warning on the "else" block. We
1252 temporarily disable the NOWARN flag, and restore it
1254 int saved_unreach = 0;
1255 if (execution_never_reaches_here && !labelexists) {
1256 saved_unreach = execution_never_reaches_here;
1257 execution_never_reaches_here |= EXECSTATE_NOWARN;
1260 /* The "else" block */
1261 parse_code_block(break_label, continue_label, 0);
1263 if (execution_never_reaches_here && !labelexists) {
1264 if (saved_unreach & EXECSTATE_NOWARN)
1265 execution_never_reaches_here |= EXECSTATE_NOWARN;
1267 execution_never_reaches_here &= ~EXECSTATE_NOWARN;
1270 /* The post-"else" label */
1271 if (ln >= 0) assemble_forward_label_no(ln2);
1275 /* There was no "else". If we're unreachable, then the
1276 statement returned unconditionally, which means
1277 "if (1) return". Skip warnings. */
1278 if (!pre_unreach && execution_never_reaches_here) {
1279 execution_never_reaches_here |= EXECSTATE_NOWARN;
1285 /* -------------------------------------------------------------------- */
1286 /* inversion ---------------------------------------------------------- */
1287 /* -------------------------------------------------------------------- */
1289 case INVERSION_CODE:
1290 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
1291 INITAOT(&AO2, SHORT_CONSTANT_OT);
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);
1300 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1301 assemblez_1(print_char_zc, temp_var1);
1303 assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1304 assemblez_1(print_char_zc, temp_var1);
1307 /* -------------------------------------------------------------------- */
1308 /* jump <label> ------------------------------------------------------- */
1309 /* -------------------------------------------------------------------- */
1312 assemblez_jump(parse_label());
1315 /* -------------------------------------------------------------------- */
1316 /* move <expression> to <expression> ---------------------------------- */
1317 /* -------------------------------------------------------------------- */
1320 misc_keywords.enabled = TRUE;
1321 AO = parse_expression(QUANTITY_CONTEXT);
1324 misc_keywords.enabled = FALSE;
1325 if ((token_type != MISC_KEYWORD_TT)
1326 || (token_value != TO_MK))
1327 { ebf_error("'to'", token_text);
1328 panic_mode_error_recovery();
1332 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1333 QUANTITY_CONTEXT, -1);
1334 AO = code_generate(AO, QUANTITY_CONTEXT, -1);
1335 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
1336 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
1337 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
1338 { if (version_number >= 5)
1339 assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
1342 { assemblez_3_to(call_zc, veneer_routine(RT__ChT_VR),
1343 AO, AO2, temp_var1);
1347 assemblez_2(insert_obj_zc, AO, AO2);
1350 /* -------------------------------------------------------------------- */
1351 /* new_line ----------------------------------------------------------- */
1352 /* -------------------------------------------------------------------- */
1354 case NEW_LINE_CODE: assemblez_0(new_line_zc); break;
1356 /* -------------------------------------------------------------------- */
1357 /* objectloop (<initialisation>) <codeblock> -------------------------- */
1358 /* -------------------------------------------------------------------- */
1360 case OBJECTLOOP_CODE:
1362 match_open_bracket();
1364 INITAOT(&AO, VARIABLE_OT);
1365 if (token_type == LOCAL_VARIABLE_TT)
1366 AO.value = token_value;
1368 if ((token_type == SYMBOL_TT) &&
1369 (symbols[token_value].type == GLOBAL_VARIABLE_T))
1370 AO.value = symbols[token_value].value;
1372 { ebf_error("'objectloop' variable", token_text);
1373 panic_mode_error_recovery(); break;
1375 if ((module_switch) && (AO.value >= MAX_LOCAL_VARIABLES)
1376 && (AO.value < LOWEST_SYSTEM_VAR_NUMBER))
1377 AO.marker = VARIABLE_MV;
1378 misc_keywords.enabled = TRUE;
1379 get_next_token(); flag = TRUE;
1380 misc_keywords.enabled = FALSE;
1381 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
1385 if ((token_type == MISC_KEYWORD_TT)
1386 && (token_value == NEAR_MK)) ln = 1;
1387 if ((token_type == MISC_KEYWORD_TT)
1388 && (token_value == FROM_MK)) ln = 2;
1389 if ((token_type == CND_TT) && (token_value == IN_COND))
1392 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
1399 { /* Old style (Inform 5) objectloops: note that we
1400 implement objectloop (a in b) in the old way since
1401 this runs through objects in a different order from
1402 the new way, and there may be existing Inform code
1404 assembly_operand AO4;
1407 sequence_point_follows = TRUE;
1408 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1409 QUANTITY_CONTEXT, -1);
1410 match_close_bracket();
1412 { INITAOTV(&AO3, VARIABLE_OT, 0);
1413 if (runtime_error_checking_switch)
1414 AO2 = check_nonzero_at_runtime(AO2, -1,
1416 assemblez_1_to(get_parent_zc, AO2, AO3);
1417 assemblez_objcode(get_child_zc, AO3, AO3, -2, TRUE);
1421 { INITAOTV(&AO3, VARIABLE_OT, 0);
1422 if (runtime_error_checking_switch)
1424 AO2 = check_nonzero_at_runtime(AO2, -1,
1427 assemblez_objcode(get_child_zc, AO2, AO3, -2, TRUE);
1430 assemblez_store(AO, AO2);
1431 assemblez_1_branch(jz_zc, AO, ln2 = next_label++, TRUE);
1432 assemble_label_no(ln4 = next_label++);
1433 parse_code_block(ln2, ln3 = next_label++, 0);
1434 sequence_point_follows = FALSE;
1435 assemble_label_no(ln3);
1436 if (runtime_error_checking_switch)
1437 { AO2 = check_nonzero_at_runtime(AO, ln2,
1440 && ((AO4.type != VARIABLE_OT)||(AO4.value != 0))
1441 && ((AO4.type != VARIABLE_OT)
1442 ||(AO4.value != AO.value)))
1443 { assembly_operand en_ao;
1444 INITAOTV(&en_ao, SHORT_CONSTANT_OT, OBJECTLOOP_BROKEN_RTE);
1445 assemblez_2_branch(jin_zc, AO, AO4,
1447 assemblez_3(call_vn_zc, veneer_routine(RT__Err_VR),
1449 assemblez_jump(ln2);
1450 assemble_label_no(next_label++);
1454 assemblez_objcode(get_sibling_zc, AO2, AO, ln4, TRUE);
1455 assemble_label_no(ln2);
1459 sequence_point_follows = TRUE;
1460 INITAOTV(&AO2, SHORT_CONSTANT_OT, 1);
1461 assemblez_store(AO, AO2);
1463 assemble_label_no(ln = next_label++);
1469 sequence_point_follows = TRUE;
1470 code_generate(parse_expression(CONDITION_CONTEXT),
1471 CONDITION_CONTEXT, ln3);
1472 match_close_bracket();
1474 parse_code_block(ln2, ln3, 0);
1476 sequence_point_follows = FALSE;
1477 assemble_label_no(ln3);
1479 INITAOTV(&AO2, LONG_CONSTANT_OT, no_objects);
1480 AO2.marker = NO_OBJS_MV;
1481 assemblez_2_branch(jg_zc, AO, AO2, ln2, TRUE);
1483 assemble_label_no(ln2);
1486 /* -------------------------------------------------------------------- */
1487 /* (see routine above) ------------------------------------------------ */
1488 /* -------------------------------------------------------------------- */
1492 parse_print_z(FALSE); return;
1493 case PRINT_RET_CODE:
1495 parse_print_z(TRUE); return;
1497 /* -------------------------------------------------------------------- */
1498 /* quit --------------------------------------------------------------- */
1499 /* -------------------------------------------------------------------- */
1501 case QUIT_CODE: assemblez_0(quit_zc); break;
1503 /* -------------------------------------------------------------------- */
1504 /* read <expression> <expression> [<Routine>] ------------------------- */
1505 /* -------------------------------------------------------------------- */
1508 INITAOTV(&AO, VARIABLE_OT, 252);
1510 code_generate(parse_expression(QUANTITY_CONTEXT),
1511 QUANTITY_CONTEXT, -1));
1512 if (version_number > 3)
1513 { INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
1514 INITAOTV(&AO4, SHORT_CONSTANT_OT, 0);
1515 assemblez_3(storeb_zc, AO, AO3, AO4);
1517 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1518 QUANTITY_CONTEXT, -1);
1521 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
1524 { if (version_number == 3)
1526 "In Version 3 no status-line drawing routine can be given");
1528 { assembly_operand AO5;
1529 /* Move the temp4 (buffer) value to the stack,
1530 since the routine might alter temp4. */
1531 assemblez_store(stack_pointer, AO);
1534 AO5 = parse_expression(CONSTANT_CONTEXT);
1536 if (version_number >= 5)
1537 assemblez_1(call_1n_zc, AO5);
1539 assemblez_1_to(call_zc, AO5, temp_var1);
1543 if (version_number > 4)
1544 { assemblez_2_to(aread_zc, AO, AO2, temp_var1);
1546 else assemblez_2(sread_zc, AO, AO2);
1549 /* -------------------------------------------------------------------- */
1550 /* remove <expression> ------------------------------------------------ */
1551 /* -------------------------------------------------------------------- */
1554 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1555 QUANTITY_CONTEXT, -1);
1556 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
1557 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
1558 { if (version_number >= 5)
1559 assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR),
1562 { assemblez_2_to(call_zc, veneer_routine(RT__ChR_VR),
1567 assemblez_1(remove_obj_zc, AO);
1570 /* -------------------------------------------------------------------- */
1571 /* restore <label> ---------------------------------------------------- */
1572 /* -------------------------------------------------------------------- */
1575 if (version_number < 5)
1576 assemblez_0_branch(restore_zc, parse_label(), TRUE);
1578 { INITAOTV(&AO2, SHORT_CONSTANT_OT, 2);
1579 assemblez_0_to(restore_zc, temp_var1);
1580 assemblez_2_branch(je_zc, temp_var1, AO2, parse_label(), TRUE);
1584 /* -------------------------------------------------------------------- */
1585 /* return [<expression>] ---------------------------------------------- */
1586 /* -------------------------------------------------------------------- */
1590 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
1591 { assemblez_0(rtrue_zc); return; }
1593 AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
1594 QUANTITY_CONTEXT, -1);
1595 if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 0)
1596 && (AO.marker == 0))
1597 { assemblez_0(rfalse_zc); break; }
1598 if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 1)
1599 && (AO.marker == 0))
1600 { assemblez_0(rtrue_zc); break; }
1601 if ((AO.type == VARIABLE_OT) && (AO.value == 0))
1602 { assemblez_0(ret_popped_zc); break; }
1603 assemblez_1(ret_zc, AO);
1606 /* -------------------------------------------------------------------- */
1607 /* rfalse ------------------------------------------------------------- */
1608 /* -------------------------------------------------------------------- */
1610 case RFALSE_CODE: assemblez_0(rfalse_zc); break;
1612 /* -------------------------------------------------------------------- */
1613 /* rtrue -------------------------------------------------------------- */
1614 /* -------------------------------------------------------------------- */
1616 case RTRUE_CODE: assemblez_0(rtrue_zc); break;
1618 /* -------------------------------------------------------------------- */
1619 /* save <label> ------------------------------------------------------- */
1620 /* -------------------------------------------------------------------- */
1623 if (version_number < 5)
1624 assemblez_0_branch(save_zc, parse_label(), TRUE);
1626 { INITAOTV(&AO, VARIABLE_OT, 255);
1627 assemblez_0_to(save_zc, AO);
1628 assemblez_1_branch(jz_zc, AO, parse_label(), FALSE);
1632 /* -------------------------------------------------------------------- */
1633 /* spaces <expression> ------------------------------------------------ */
1634 /* -------------------------------------------------------------------- */
1637 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1638 QUANTITY_CONTEXT, -1);
1639 INITAOTV(&AO2, VARIABLE_OT, 255);
1641 assemblez_store(AO2, AO);
1643 INITAOTV(&AO, SHORT_CONSTANT_OT, 32);
1644 INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
1646 assemblez_2_branch(jl_zc, AO2, AO3, ln = next_label++, TRUE);
1647 assemble_label_no(ln2 = next_label++);
1648 assemblez_1(print_char_zc, AO);
1650 assemblez_1_branch(jz_zc, AO2, ln2, FALSE);
1651 assemble_label_no(ln);
1654 /* -------------------------------------------------------------------- */
1655 /* string <expression> <literal-string> ------------------------------- */
1656 /* -------------------------------------------------------------------- */
1659 INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
1660 INITAOTV(&AO2, SHORT_CONSTANT_OT, 12);
1661 INITAOTV(&AO3, VARIABLE_OT, 252);
1662 assemblez_2_to(loadw_zc, AO, AO2, AO3);
1663 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1664 QUANTITY_CONTEXT, -1);
1665 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
1666 /* Compile-time check */
1667 if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
1668 error_max_dynamic_strings(AO2.value);
1673 if (token_type == DQ_TT)
1674 { INITAOT(&AO4, LONG_CONSTANT_OT);
1675 /* This string must be in low memory so that the
1676 dynamic string table can refer to it. */
1677 AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
1681 AO4 = parse_expression(CONSTANT_CONTEXT);
1683 assemblez_3(storew_zc, AO3, AO2, AO4);
1686 /* -------------------------------------------------------------------- */
1687 /* style roman/reverse/bold/underline/fixed --------------------------- */
1688 /* -------------------------------------------------------------------- */
1691 if (version_number==3)
1693 "The 'style' statement cannot be used for Version 3 games");
1694 panic_mode_error_recovery();
1698 misc_keywords.enabled = TRUE;
1700 misc_keywords.enabled = FALSE;
1701 if ((token_type != MISC_KEYWORD_TT)
1702 || ((token_value != ROMAN_MK)
1703 && (token_value != REVERSE_MK)
1704 && (token_value != BOLD_MK)
1705 && (token_value != UNDERLINE_MK)
1706 && (token_value != FIXED_MK)))
1708 "'roman', 'bold', 'underline', 'reverse' or 'fixed'",
1710 panic_mode_error_recovery();
1714 INITAOT(&AO, SHORT_CONSTANT_OT);
1716 { case ROMAN_MK: AO.value = 0; break;
1717 case REVERSE_MK: AO.value = 1; break;
1718 case BOLD_MK: AO.value = 2; break;
1719 case UNDERLINE_MK: AO.value = 4; break;
1720 case FIXED_MK: AO.value = 8; break;
1722 assemblez_1(set_text_style_zc, AO); break;
1724 /* -------------------------------------------------------------------- */
1725 /* switch (<expression>) <codeblock> ---------------------------------- */
1726 /* -------------------------------------------------------------------- */
1729 match_open_bracket();
1730 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1731 QUANTITY_CONTEXT, -1);
1732 match_close_bracket();
1734 INITAOTV(&AO2, VARIABLE_OT, 255);
1735 assemblez_store(AO2, AO);
1737 parse_code_block(ln = next_label++, continue_label, 1);
1738 assemble_forward_label_no(ln);
1741 /* -------------------------------------------------------------------- */
1742 /* while (<condition>) <codeblock> ------------------------------------ */
1743 /* -------------------------------------------------------------------- */
1746 assemble_label_no(ln = next_label++);
1747 match_open_bracket();
1749 code_generate(parse_expression(CONDITION_CONTEXT),
1750 CONDITION_CONTEXT, ln2 = next_label++);
1751 match_close_bracket();
1753 parse_code_block(ln2, ln, 0);
1754 sequence_point_follows = FALSE;
1756 assemble_forward_label_no(ln2);
1759 /* -------------------------------------------------------------------- */
1762 error("'default' without matching 'switch'"); break;
1764 error("'else' without matching 'if'"); break;
1766 error("'until' without matching 'do'");
1767 panic_mode_error_recovery(); return;
1770 StatementTerminator:
1773 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
1774 { ebf_error("';'", token_text);
1779 static void parse_statement_g(int break_label, int continue_label)
1780 { int ln, ln2, ln3, ln4, flag, onstack;
1781 int pre_unreach, labelexists;
1782 assembly_operand AO, AO2, AO3, AO4;
1783 debug_location spare_debug_location1, spare_debug_location2;
1787 if ((token_type == SEP_TT) && (token_value == HASH_SEP))
1788 { parse_directive(TRUE);
1789 parse_statement(break_label, continue_label); return;
1792 if ((token_type == SEP_TT) && (token_value == AT_SEP))
1793 { parse_assembly(); return;
1796 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
1798 if (token_type == DQ_TT)
1799 { parse_print_g(TRUE); return;
1802 if ((token_type == SEP_TT) && (token_value == LESS_SEP))
1803 { parse_action(); goto StatementTerminator; }
1805 if (token_type == EOF_TT)
1806 { ebf_error("statement", token_text); return; }
1808 if (token_type != STATEMENT_TT)
1810 AO = parse_expression(VOID_CONTEXT);
1811 code_generate(AO, VOID_CONTEXT, -1);
1812 if (vivc_flag) { panic_mode_error_recovery(); return; }
1813 goto StatementTerminator;
1816 statements.enabled = FALSE;
1821 /* -------------------------------------------------------------------- */
1822 /* box <string-1> ... <string-n> -------------------------------------- */
1823 /* -------------------------------------------------------------------- */
1826 INITAOT(&AO3, CONSTANT_OT);
1827 AO3.value = begin_table_array();
1828 AO3.marker = ARRAY_MV;
1832 if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
1834 if (token_type != DQ_TT)
1835 ebf_error("text of box line in double-quotes",
1838 for (i=0, j=0; token_text[i] != 0; j++)
1839 if (token_text[i] == '@')
1840 { if (token_text[i+1] == '@')
1842 while (isdigit(token_text[i])) i++;
1846 if (token_text[i] != 0) i++;
1847 if (token_text[i] != 0) i++;
1851 if (j > ln2) ln2 = j;
1854 array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
1856 finish_array(ln, FALSE);
1858 error("No lines of text given for 'box' display");
1861 AO2.value = ln2; set_constant_ot(&AO2);
1862 assembleg_call_2(veneer_routine(Box__Routine_VR),
1863 AO2, AO3, zero_operand);
1866 /* -------------------------------------------------------------------- */
1867 /* break -------------------------------------------------------------- */
1868 /* -------------------------------------------------------------------- */
1871 if (break_label == -1)
1872 error("'break' can only be used in a loop or 'switch' block");
1874 assembleg_jump(break_label);
1877 /* -------------------------------------------------------------------- */
1878 /* continue ----------------------------------------------------------- */
1879 /* -------------------------------------------------------------------- */
1882 if (continue_label == -1)
1883 error("'continue' can only be used in a loop block");
1885 assembleg_jump(continue_label);
1888 /* -------------------------------------------------------------------- */
1889 /* do <codeblock> until (<condition>) --------------------------------- */
1890 /* -------------------------------------------------------------------- */
1893 assemble_label_no(ln = next_label++);
1894 ln2 = next_label++; ln3 = next_label++;
1895 parse_code_block(ln3, ln2, 0);
1896 statements.enabled = TRUE;
1898 if ((token_type == STATEMENT_TT)
1899 && (token_value == UNTIL_CODE))
1900 { assemble_forward_label_no(ln2);
1901 match_open_bracket();
1902 AO = parse_expression(CONDITION_CONTEXT);
1903 match_close_bracket();
1904 code_generate(AO, CONDITION_CONTEXT, ln);
1906 else error("'do' without matching 'until'");
1908 assemble_forward_label_no(ln3);
1911 /* -------------------------------------------------------------------- */
1912 /* font on/off -------------------------------------------------------- */
1913 /* -------------------------------------------------------------------- */
1916 misc_keywords.enabled = TRUE;
1918 misc_keywords.enabled = FALSE;
1919 if ((token_type != MISC_KEYWORD_TT)
1920 || ((token_value != ON_MK)
1921 && (token_value != OFF_MK)))
1922 { ebf_error("'on' or 'off'", token_text);
1923 panic_mode_error_recovery();
1927 /* Call glk_set_style(normal or preformatted) */
1930 set_constant_ot(&AO);
1931 if (token_value == ON_MK)
1935 assembleg_call_2(veneer_routine(Glk__Wrap_VR),
1936 AO, AO2, zero_operand);
1939 /* -------------------------------------------------------------------- */
1940 /* for (<initialisation> : <continue-condition> : <updating>) --------- */
1941 /* -------------------------------------------------------------------- */
1943 /* Note that it's legal for any or all of the three sections of a
1944 'for' specification to be empty. This 'for' implementation
1945 often wastes 3 bytes with a redundant branch rather than keep
1946 expression parse trees for long periods (as previous versions
1947 of Inform did, somewhat crudely by simply storing the textual
1948 form of a 'for' loop). It is adequate for now. */
1951 match_open_bracket();
1954 /* Initialisation code */
1955 AO.type = OMITTED_OT;
1956 spare_debug_location1 = statement_debug_location;
1957 AO2.type = OMITTED_OT; flag = 0;
1958 spare_debug_location2 = statement_debug_location;
1960 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1962 if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
1963 { sequence_point_follows = TRUE;
1964 statement_debug_location = get_token_location();
1965 code_generate(parse_expression(FORINIT_CONTEXT),
1969 if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
1971 if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
1972 { assemble_label_no(ln = next_label++);
1974 parse_code_block(ln2, ln, 0);
1975 sequence_point_follows = FALSE;
1976 if (!execution_never_reaches_here)
1978 assemble_forward_label_no(ln2);
1984 if (!match_colon()) break;
1988 if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1990 spare_debug_location1 = get_token_location();
1991 AO = parse_expression(CONDITION_CONTEXT);
1992 if (!match_colon()) break;
1997 if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
1999 spare_debug_location2 = get_token_location();
2000 AO2 = parse_expression(VOID_CONTEXT);
2001 match_close_bracket();
2002 flag = test_for_incdec(AO2);
2009 if ((AO2.type == OMITTED_OT) || (flag != 0))
2011 assemble_label_no(ln);
2012 if (flag==0) assemble_label_no(ln2);
2014 /* The "finished yet?" condition */
2016 if (AO.type != OMITTED_OT)
2017 { sequence_point_follows = TRUE;
2018 statement_debug_location = spare_debug_location1;
2019 code_generate(AO, CONDITION_CONTEXT, ln3);
2025 /* This is the jump which could be avoided with the aid
2026 of long-term expression storage */
2028 sequence_point_follows = FALSE;
2029 assembleg_jump(ln2);
2031 /* The "update" part */
2033 assemble_label_no(ln);
2034 sequence_point_follows = TRUE;
2035 statement_debug_location = spare_debug_location2;
2036 code_generate(AO2, VOID_CONTEXT, -1);
2038 assemble_label_no(ln2);
2040 /* The "finished yet?" condition */
2042 if (AO.type != OMITTED_OT)
2043 { sequence_point_follows = TRUE;
2044 statement_debug_location = spare_debug_location1;
2045 code_generate(AO, CONDITION_CONTEXT, ln3);
2051 /* In this optimised case, update code is at the end
2052 of the loop block, so "continue" goes there */
2054 parse_code_block(ln3, ln2, 0);
2055 assemble_label_no(ln2);
2057 sequence_point_follows = TRUE;
2058 statement_debug_location = spare_debug_location2;
2062 if (AO3.value >= MAX_LOCAL_VARIABLES)
2063 AO3.type = GLOBALVAR_OT;
2065 AO3.type = LOCALVAR_OT;
2066 assembleg_3(add_gc, AO3, one_operand, AO3);
2071 if (AO3.value >= MAX_LOCAL_VARIABLES)
2072 AO3.type = GLOBALVAR_OT;
2074 AO3.type = LOCALVAR_OT;
2075 assembleg_3(sub_gc, AO3, one_operand, AO3);
2081 /* In the unoptimised case, update code is at the
2082 start of the loop block, so "continue" goes there */
2084 parse_code_block(ln3, ln, 0);
2085 if (!execution_never_reaches_here)
2086 { sequence_point_follows = FALSE;
2091 assemble_forward_label_no(ln3);
2094 /* -------------------------------------------------------------------- */
2095 /* give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
2096 /* -------------------------------------------------------------------- */
2099 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2100 QUANTITY_CONTEXT, -1);
2101 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
2102 if ((AO.type == LOCALVAR_OT) && (AO.value == 0))
2109 if ((token_type == SEP_TT)
2110 && (token_value == SEMICOLON_SEP)) {
2112 assembleg_2(copy_gc, stack_pointer, zero_operand);
2116 if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
2122 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2123 QUANTITY_CONTEXT, -1);
2124 check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
2125 if (runtime_error_checking_switch && (!veneer_mode))
2126 { ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR);
2127 if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
2128 /* already on stack */
2131 assembleg_store(stack_pointer, AO2);
2134 assembleg_2(stkpeek_gc, one_operand, stack_pointer);
2136 assembleg_store(stack_pointer, AO);
2137 assembleg_3(call_gc, veneer_routine(ln2), two_operand,
2141 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
2143 set_constant_ot(&AO2);
2146 INITAOTV(&AO3, BYTECONSTANT_OT, 8);
2147 assembleg_3(add_gc, AO2, AO3, stack_pointer);
2148 AO2 = stack_pointer;
2151 if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0))
2152 assembleg_2(stkpeek_gc, one_operand,
2155 assembleg_2(stkpeek_gc, zero_operand,
2162 assembleg_3(astorebit_gc, AO, AO2, AO3);
2166 /* -------------------------------------------------------------------- */
2167 /* if (<condition>) <codeblock> [else <codeblock>] -------------------- */
2168 /* -------------------------------------------------------------------- */
2171 flag = FALSE; /* set if there's an "else" */
2173 pre_unreach = execution_never_reaches_here;
2175 match_open_bracket();
2176 AO = parse_expression(CONDITION_CONTEXT);
2177 match_close_bracket();
2179 statements.enabled = TRUE;
2181 if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
2184 if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
2192 code_generate(AO, CONDITION_CONTEXT, ln);
2194 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
2195 /* If the condition never falls through to here, then
2196 it was an "if (0)" test. Our convention is to skip
2197 the "not reached" warnings for this case. */
2198 execution_never_reaches_here |= EXECSTATE_NOWARN;
2201 /* The "if" block */
2202 if (ln >= 0) parse_code_block(break_label, continue_label, 0);
2205 if ((token_type != SEP_TT)
2206 || (token_value != SEMICOLON_SEP))
2207 { ebf_error("';'", token_text);
2212 statements.enabled = TRUE;
2215 /* An #if directive around the ELSE clause is legal. */
2216 while ((token_type == SEP_TT) && (token_value == HASH_SEP))
2217 { parse_directive(TRUE);
2218 statements.enabled = TRUE;
2222 if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
2225 { ln2 = next_label++;
2226 if (!execution_never_reaches_here)
2227 { sequence_point_follows = FALSE;
2228 assembleg_jump(ln2);
2232 else put_token_back();
2234 /* The "else" label (or end of statement, if there is no "else") */
2235 labelexists = FALSE;
2236 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
2240 /* If labelexists is false, then we started with
2241 "if (1)". In this case, we don't want a "not
2242 reached" warning on the "else" block. We
2243 temporarily disable the NOWARN flag, and restore it
2245 int saved_unreach = 0;
2246 if (execution_never_reaches_here && !labelexists) {
2247 saved_unreach = execution_never_reaches_here;
2248 execution_never_reaches_here |= EXECSTATE_NOWARN;
2251 /* The "else" block */
2252 parse_code_block(break_label, continue_label, 0);
2254 if (execution_never_reaches_here && !labelexists) {
2255 if (saved_unreach & EXECSTATE_NOWARN)
2256 execution_never_reaches_here |= EXECSTATE_NOWARN;
2258 execution_never_reaches_here &= ~EXECSTATE_NOWARN;
2261 /* The post-"else" label */
2262 if (ln >= 0) assemble_forward_label_no(ln2);
2266 /* There was no "else". If we're unreachable, then the
2267 statement returned unconditionally, which means
2268 "if (1) return". Skip warnings. */
2269 if (!pre_unreach && execution_never_reaches_here) {
2270 execution_never_reaches_here |= EXECSTATE_NOWARN;
2276 /* -------------------------------------------------------------------- */
2277 /* inversion ---------------------------------------------------------- */
2278 /* -------------------------------------------------------------------- */
2280 case INVERSION_CODE:
2281 INITAOTV(&AO2, DEREFERENCE_OT, GLULX_HEADER_SIZE+8);
2282 assembleg_2(copyb_gc, AO2, stack_pointer);
2283 assembleg_1(streamchar_gc, stack_pointer);
2284 AO2.value = GLULX_HEADER_SIZE+9;
2285 assembleg_2(copyb_gc, AO2, stack_pointer);
2286 assembleg_1(streamchar_gc, stack_pointer);
2287 AO2.value = GLULX_HEADER_SIZE+10;
2288 assembleg_2(copyb_gc, AO2, stack_pointer);
2289 assembleg_1(streamchar_gc, stack_pointer);
2290 AO2.value = GLULX_HEADER_SIZE+11;
2291 assembleg_2(copyb_gc, AO2, stack_pointer);
2292 assembleg_1(streamchar_gc, stack_pointer);
2294 if (/* DISABLES CODE */ (0)) {
2297 set_constant_ot(&AO);
2298 assembleg_1(streamchar_gc, AO);
2300 set_constant_ot(&AO);
2301 assembleg_1(streamchar_gc, AO);
2303 AO2.value = GLULX_HEADER_SIZE+12;
2304 assembleg_2(copyb_gc, AO2, stack_pointer);
2305 assembleg_1(streamchar_gc, stack_pointer);
2306 AO2.value = GLULX_HEADER_SIZE+13;
2307 assembleg_2(copyb_gc, AO2, stack_pointer);
2308 assembleg_1(streamchar_gc, stack_pointer);
2309 AO2.value = GLULX_HEADER_SIZE+14;
2310 assembleg_2(copyb_gc, AO2, stack_pointer);
2311 assembleg_1(streamchar_gc, stack_pointer);
2312 AO2.value = GLULX_HEADER_SIZE+15;
2313 assembleg_2(copyb_gc, AO2, stack_pointer);
2314 assembleg_1(streamchar_gc, stack_pointer);
2318 set_constant_ot(&AO);
2319 assembleg_1(streamchar_gc, AO);
2324 /* -------------------------------------------------------------------- */
2325 /* jump <label> ------------------------------------------------------- */
2326 /* -------------------------------------------------------------------- */
2329 assembleg_jump(parse_label());
2332 /* -------------------------------------------------------------------- */
2333 /* move <expression> to <expression> ---------------------------------- */
2334 /* -------------------------------------------------------------------- */
2337 misc_keywords.enabled = TRUE;
2338 AO = parse_expression(QUANTITY_CONTEXT);
2341 misc_keywords.enabled = FALSE;
2342 if ((token_type != MISC_KEYWORD_TT)
2343 || (token_value != TO_MK))
2344 { ebf_error("'to'", token_text);
2345 panic_mode_error_recovery();
2349 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2350 QUANTITY_CONTEXT, -1);
2351 AO = code_generate(AO, QUANTITY_CONTEXT, -1);
2352 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
2353 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
2354 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
2355 assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2,
2358 assembleg_call_2(veneer_routine(OB__Move_VR), AO, AO2,
2362 /* -------------------------------------------------------------------- */
2363 /* new_line ----------------------------------------------------------- */
2364 /* -------------------------------------------------------------------- */
2367 INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
2368 assembleg_1(streamchar_gc, AO);
2371 /* -------------------------------------------------------------------- */
2372 /* objectloop (<initialisation>) <codeblock> -------------------------- */
2373 /* -------------------------------------------------------------------- */
2375 case OBJECTLOOP_CODE:
2377 match_open_bracket();
2379 if (token_type == LOCAL_VARIABLE_TT) {
2380 INITAOTV(&AO, LOCALVAR_OT, token_value);
2382 else if ((token_type == SYMBOL_TT) &&
2383 (symbols[token_value].type == GLOBAL_VARIABLE_T)) {
2384 INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
2387 ebf_error("'objectloop' variable", token_text);
2388 panic_mode_error_recovery();
2391 misc_keywords.enabled = TRUE;
2392 get_next_token(); flag = TRUE;
2393 misc_keywords.enabled = FALSE;
2394 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
2398 if ((token_type == MISC_KEYWORD_TT)
2399 && (token_value == NEAR_MK)) ln = 1;
2400 if ((token_type == MISC_KEYWORD_TT)
2401 && (token_value == FROM_MK)) ln = 2;
2402 if ((token_type == CND_TT) && (token_value == IN_COND))
2405 if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
2412 /* Old style (Inform 5) objectloops: note that we
2413 implement objectloop (a in b) in the old way since
2414 this runs through objects in a different order from
2415 the new way, and there may be existing Inform code
2417 assembly_operand AO4, AO5;
2420 sequence_point_follows = TRUE;
2421 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2422 QUANTITY_CONTEXT, -1);
2423 match_close_bracket();
2425 if (runtime_error_checking_switch)
2426 AO2 = check_nonzero_at_runtime(AO2, -1,
2428 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
2429 assembleg_3(aload_gc, AO2, AO4, stack_pointer);
2430 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
2431 assembleg_3(aload_gc, stack_pointer, AO4, stack_pointer);
2432 AO2 = stack_pointer;
2435 if (runtime_error_checking_switch) {
2437 AO2 = check_nonzero_at_runtime(AO2, -1,
2440 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
2441 assembleg_3(aload_gc, AO2, AO4, stack_pointer);
2442 AO2 = stack_pointer;
2447 assembleg_store(AO, AO2);
2448 assembleg_1_branch(jz_gc, AO, ln2 = next_label++);
2449 assemble_label_no(ln4 = next_label++);
2450 parse_code_block(ln2, ln3 = next_label++, 0);
2451 sequence_point_follows = FALSE;
2452 assemble_label_no(ln3);
2453 if (runtime_error_checking_switch) {
2454 AO2 = check_nonzero_at_runtime(AO, ln2,
2457 && ((AO5.type != LOCALVAR_OT)||(AO5.value != 0))
2458 && ((AO5.type != LOCALVAR_OT)||(AO5.value != AO.value)))
2459 { assembly_operand en_ao;
2461 en_ao.value = OBJECTLOOP_BROKEN_RTE;
2462 set_constant_ot(&en_ao);
2463 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
2464 assembleg_3(aload_gc, AO, AO4, stack_pointer);
2465 assembleg_2_branch(jeq_gc, stack_pointer, AO5,
2467 assembleg_call_2(veneer_routine(RT__Err_VR),
2468 en_ao, AO, zero_operand);
2469 assembleg_jump(ln2);
2470 assemble_label_no(next_label++);
2476 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_SIBLING());
2477 assembleg_3(aload_gc, AO2, AO4, AO);
2478 assembleg_1_branch(jnz_gc, AO, ln4);
2479 assemble_label_no(ln2);
2483 sequence_point_follows = TRUE;
2484 ln = symbol_index("Class", -1);
2485 INITAOT(&AO2, CONSTANT_OT);
2486 AO2.value = symbols[ln].value;
2487 AO2.marker = OBJECT_MV;
2488 assembleg_store(AO, AO2);
2490 assemble_label_no(ln = next_label++);
2496 sequence_point_follows = TRUE;
2497 code_generate(parse_expression(CONDITION_CONTEXT),
2498 CONDITION_CONTEXT, ln3);
2499 match_close_bracket();
2501 parse_code_block(ln2, ln3, 0);
2503 sequence_point_follows = FALSE;
2504 assemble_label_no(ln3);
2505 INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHAIN());
2506 assembleg_3(aload_gc, AO, AO4, AO);
2507 assembleg_1_branch(jnz_gc, AO, ln);
2508 assemble_label_no(ln2);
2511 /* -------------------------------------------------------------------- */
2512 /* (see routine above) ------------------------------------------------ */
2513 /* -------------------------------------------------------------------- */
2517 parse_print_g(FALSE); return;
2518 case PRINT_RET_CODE:
2520 parse_print_g(TRUE); return;
2522 /* -------------------------------------------------------------------- */
2523 /* quit --------------------------------------------------------------- */
2524 /* -------------------------------------------------------------------- */
2527 assembleg_0(quit_gc); break;
2529 /* -------------------------------------------------------------------- */
2530 /* remove <expression> ------------------------------------------------ */
2531 /* -------------------------------------------------------------------- */
2534 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2535 QUANTITY_CONTEXT, -1);
2536 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
2537 if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
2538 assembleg_call_1(veneer_routine(RT__ChR_VR), AO,
2541 assembleg_call_1(veneer_routine(OB__Remove_VR), AO,
2545 /* -------------------------------------------------------------------- */
2546 /* return [<expression>] ---------------------------------------------- */
2547 /* -------------------------------------------------------------------- */
2551 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) {
2552 assembleg_1(return_gc, one_operand);
2556 AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
2557 QUANTITY_CONTEXT, -1);
2558 assembleg_1(return_gc, AO);
2561 /* -------------------------------------------------------------------- */
2562 /* rfalse ------------------------------------------------------------- */
2563 /* -------------------------------------------------------------------- */
2566 assembleg_1(return_gc, zero_operand);
2569 /* -------------------------------------------------------------------- */
2570 /* rtrue -------------------------------------------------------------- */
2571 /* -------------------------------------------------------------------- */
2574 assembleg_1(return_gc, one_operand);
2577 /* -------------------------------------------------------------------- */
2578 /* spaces <expression> ------------------------------------------------ */
2579 /* -------------------------------------------------------------------- */
2582 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2583 QUANTITY_CONTEXT, -1);
2585 assembleg_store(temp_var1, AO);
2588 AO.value = 32; set_constant_ot(&AO);
2590 assembleg_2_branch(jlt_gc, temp_var1, one_operand,
2592 assemble_label_no(ln2 = next_label++);
2593 assembleg_1(streamchar_gc, AO);
2594 assembleg_dec(temp_var1);
2595 assembleg_1_branch(jnz_gc, temp_var1, ln2);
2596 assemble_label_no(ln);
2599 /* -------------------------------------------------------------------- */
2600 /* string <expression> <literal-string> ------------------------------- */
2601 /* -------------------------------------------------------------------- */
2604 AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2605 QUANTITY_CONTEXT, -1);
2606 if (is_constant_ot(AO2.type) && AO2.marker == 0) {
2607 /* Compile-time check */
2608 if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
2609 error_max_dynamic_strings(AO2.value);
2613 if (token_type == DQ_TT)
2614 { INITAOT(&AO4, CONSTANT_OT);
2615 /* This is not actually placed in low memory; Glulx
2616 has no such concept. We use the LOWSTRING flag
2617 for compatibility with older compiler behavior. */
2618 AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
2619 AO4.marker = STRING_MV;
2623 AO4 = parse_expression(CONSTANT_CONTEXT);
2625 assembleg_call_2(veneer_routine(Dynam__String_VR),
2626 AO2, AO4, zero_operand);
2629 /* -------------------------------------------------------------------- */
2630 /* style roman/reverse/bold/underline/fixed --------------------------- */
2631 /* -------------------------------------------------------------------- */
2634 misc_keywords.enabled = TRUE;
2636 misc_keywords.enabled = FALSE;
2637 if ((token_type != MISC_KEYWORD_TT)
2638 || ((token_value != ROMAN_MK)
2639 && (token_value != REVERSE_MK)
2640 && (token_value != BOLD_MK)
2641 && (token_value != UNDERLINE_MK)
2642 && (token_value != FIXED_MK)))
2644 "'roman', 'bold', 'underline', 'reverse' or 'fixed'",
2646 panic_mode_error_recovery();
2650 /* Call glk_set_style() */
2654 set_constant_ot(&AO);
2658 AO2 = zero_operand; /* normal */
2662 AO2.value = 5; /* alert */
2663 set_constant_ot(&AO2);
2667 AO2.value = 4; /* subheader */
2668 set_constant_ot(&AO2);
2671 AO2 = one_operand; /* emphasized */
2674 AO2 = two_operand; /* preformatted */
2677 assembleg_call_2(veneer_routine(Glk__Wrap_VR),
2678 AO, AO2, zero_operand);
2681 /* -------------------------------------------------------------------- */
2682 /* switch (<expression>) <codeblock> ---------------------------------- */
2683 /* -------------------------------------------------------------------- */
2686 match_open_bracket();
2687 AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2688 QUANTITY_CONTEXT, -1);
2689 match_close_bracket();
2691 assembleg_store(temp_var1, AO);
2693 parse_code_block(ln = next_label++, continue_label, 1);
2694 assemble_forward_label_no(ln);
2697 /* -------------------------------------------------------------------- */
2698 /* while (<condition>) <codeblock> ------------------------------------ */
2699 /* -------------------------------------------------------------------- */
2702 assemble_label_no(ln = next_label++);
2703 match_open_bracket();
2705 code_generate(parse_expression(CONDITION_CONTEXT),
2706 CONDITION_CONTEXT, ln2 = next_label++);
2707 match_close_bracket();
2709 parse_code_block(ln2, ln, 0);
2710 sequence_point_follows = FALSE;
2712 assemble_forward_label_no(ln2);
2715 /* -------------------------------------------------------------------- */
2718 error("'default' without matching 'switch'"); break;
2720 error("'else' without matching 'if'"); break;
2722 error("'until' without matching 'do'");
2723 panic_mode_error_recovery(); return;
2725 /* -------------------------------------------------------------------- */
2727 /* And a useful default, which will never be triggered in a complete
2728 Inform compiler, but which is important in development. */
2731 error("*** Statement code gen: Can't generate yet ***\n");
2732 panic_mode_error_recovery(); return;
2735 StatementTerminator:
2738 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
2739 { ebf_error("';'", token_text);
2744 extern void parse_statement(int break_label, int continue_label)
2747 int saved_entire_flag;
2749 res = parse_named_label_statements();
2753 saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
2754 if (execution_never_reaches_here)
2755 execution_never_reaches_here |= EXECSTATE_ENTIRE;
2758 parse_statement_z(break_label, continue_label);
2760 parse_statement_g(break_label, continue_label);
2762 if (saved_entire_flag)
2763 execution_never_reaches_here |= EXECSTATE_ENTIRE;
2765 execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
2768 /* ========================================================================= */
2769 /* Data structure management routines */
2770 /* ------------------------------------------------------------------------- */
2772 extern void init_states_vars(void)
2776 extern void states_begin_pass(void)
2780 extern void states_allocate_arrays(void)
2784 extern void states_free_arrays(void)
2788 /* ========================================================================= */