Update to Inform v6.42
[inform.git] / src / states.c
1 /* ------------------------------------------------------------------------- */
2 /*   "states" :  Statement translator                                        */
3 /*                                                                           */
4 /*   Part of Inform 6.42                                                     */
5 /*   copyright (c) Graham Nelson 1993 - 2024                                 */
6 /*                                                                           */
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.                                       */
11 /*                                                                           */
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.                              */
16 /*                                                                           */
17 /* You should have received a copy of the GNU General Public License         */
18 /* along with Inform. If not, see https://gnu.org/licenses/                  */
19 /*                                                                           */
20 /* ------------------------------------------------------------------------- */
21
22 #include "header.h"
23
24 static int match_colon(void)
25 {   get_next_token();
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 ':'");
30         else
31         if (token_value != COLON_SEP)
32         {   ebf_curtoken_error("':'");
33             panic_mode_error_recovery();
34             return(FALSE);
35         }
36     }
37     else
38     {   ebf_curtoken_error("':'");
39         panic_mode_error_recovery();
40         return(FALSE);
41     }
42     return(TRUE);
43 }
44
45 static void match_open_bracket(void)
46 {   get_next_token();
47     if ((token_type == SEP_TT) && (token_value == OPENB_SEP)) return;
48     put_token_back();
49     ebf_curtoken_error("'('");
50 }
51
52 extern void match_close_bracket(void)
53 {   get_next_token();
54     if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP)) return;
55     put_token_back();
56     ebf_curtoken_error("')'");
57 }
58
59 static void parse_action(void)
60 {   int level = 1, args = 0, codegen_action;
61     assembly_operand AO, AO2, AO3, AO4, AO5;
62
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".)
67
68        The R_Process() function should be supplied by the library, 
69        although a stub is defined in the veneer.
70
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>.)
75
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.
80
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,
84        so we can't do it.
85     */
86
87     dont_enter_into_symbol_table = TRUE;
88     get_next_token();
89     if ((token_type == SEP_TT) && (token_value == LESS_SEP))
90     {   level = 2; get_next_token();
91     }
92     dont_enter_into_symbol_table = FALSE;
93
94     /* Peek at the next token; see if it's an open-paren. */
95     if ((token_type==SEP_TT) && (token_value==OPENB_SEP))
96     {   put_token_back();
97         AO2 = parse_expression(ACTION_Q_CONTEXT);
98         codegen_action = TRUE;
99     }
100     else
101     {
102         if (token_type != UQ_TT) {
103             ebf_curtoken_error("name of action");
104         }
105         codegen_action = FALSE;
106         AO2 = action_of_name(token_text);
107     }
108
109     get_next_token();
110     AO3 = zero_operand;
111     AO4 = zero_operand;
112     AO5 = zero_operand;
113     if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
114     {   put_token_back();
115         args = 1;
116         AO3 = parse_expression(ACTION_Q_CONTEXT);
117
118         get_next_token();
119     }
120     if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
121     {   put_token_back();
122         args = 2;
123         AO4 = parse_expression(QUANTITY_CONTEXT);
124         get_next_token();
125     }
126     if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP)))
127     {
128         ebf_curtoken_error("',' or '>'");
129     }
130
131     if ((token_type == SEP_TT) && (token_value == COMMA_SEP))
132     {
133         if (!glulx_mode && (version_number < 4))
134         {
135             error("<x, y> syntax is not available in Z-code V3 or earlier");
136         }
137         args = 3;
138         AO5 = parse_expression(QUANTITY_CONTEXT);
139         get_next_token();
140         if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
141         {
142             ebf_curtoken_error("'>'");
143         }
144     }
145
146     if (level == 2)
147     {   get_next_token();
148         if (!((token_type == SEP_TT) && (token_value == GREATER_SEP)))
149         {   put_token_back();
150             ebf_curtoken_error("'>>'");
151         }
152     }
153
154     if (!glulx_mode) {
155
156       AO = veneer_routine(R_Process_VR);
157
158       switch(args)
159       {   case 0:
160             if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
161             if (version_number>=5)
162                 assemblez_2(call_2n_zc, AO, AO2);
163             else
164             if (version_number==4)
165                 assemblez_2_to(call_vs_zc, AO, AO2, temp_var1);
166             else
167                 assemblez_2_to(call_zc, AO, AO2, temp_var1);
168             break;
169           case 1:
170             AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
171             if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
172             if (version_number>=5)
173                 assemblez_3(call_vn_zc, AO, AO2, AO3);
174             else
175             if (version_number==4)
176                 assemblez_3_to(call_vs_zc, AO, AO2, AO3, temp_var1);
177             else
178                 assemblez_3_to(call_zc, AO, AO2, AO3, temp_var1);
179             break;
180           case 2:
181             AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
182             AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
183             if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
184             if (version_number>=5)
185                 assemblez_4(call_vn_zc, AO, AO2, AO3, AO4);
186             else
187             if (version_number==4)
188                 assemblez_4_to(call_vs_zc, AO, AO2, AO3, AO4, temp_var1);
189             else
190                 assemblez_4_to(call_zc, AO, AO2, AO3, AO4, temp_var1);
191             break;
192           case 3:
193             AO5 = code_generate(AO5, QUANTITY_CONTEXT, -1);
194             AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
195             AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
196             if (codegen_action) AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
197             if (version_number>=5)
198                 assemblez_5(call_vn2_zc, AO, AO2, AO3, AO4, AO5);
199             else
200             if (version_number==4)
201                 assemblez_5_to(call_vs2_zc, AO, AO2, AO3, AO4, AO5, temp_var1);
202             /* if V3 or earlier, we've already displayed an error */
203             break;
204             break;
205       }
206
207       if (level == 2) assemblez_0(rtrue_zc);
208
209     }
210     else {
211
212       AO = veneer_routine(R_Process_VR);
213
214       switch (args) {
215
216       case 0:
217         if (codegen_action) 
218           AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
219         assembleg_call_1(AO, AO2, zero_operand);
220         break;
221
222       case 1:
223         AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
224         if (codegen_action)
225           AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
226         assembleg_call_2(AO, AO2, AO3, zero_operand);
227         break;
228
229       case 2:
230         AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
231         AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
232         if (codegen_action) 
233           AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
234         assembleg_call_3(AO, AO2, AO3, AO4, zero_operand);
235         break;
236
237       case 3:
238         AO5 = code_generate(AO5, QUANTITY_CONTEXT, -1);
239         if (!((AO5.type == LOCALVAR_OT) && (AO5.value == 0)))
240             assembleg_store(stack_pointer, AO5);
241         AO4 = code_generate(AO4, QUANTITY_CONTEXT, -1);
242         if (!((AO4.type == LOCALVAR_OT) && (AO4.value == 0)))
243             assembleg_store(stack_pointer, AO4);
244         AO3 = code_generate(AO3, QUANTITY_CONTEXT, -1);
245         if (!((AO3.type == LOCALVAR_OT) && (AO3.value == 0)))
246             assembleg_store(stack_pointer, AO3);
247         if (codegen_action) 
248           AO2 = code_generate(AO2, QUANTITY_CONTEXT, -1);
249         if (!((AO2.type == LOCALVAR_OT) && (AO2.value == 0)))
250           assembleg_store(stack_pointer, AO2);
251         assembleg_3(call_gc, AO, four_operand, zero_operand);
252         break;
253       }
254
255       if (level == 2) 
256         assembleg_1(return_gc, one_operand);
257
258     }
259 }
260
261 extern int parse_label(void)
262 {
263     get_next_token();
264
265     if ((token_type == SYMBOL_TT) &&
266         (symbols[token_value].type == LABEL_T))
267     {   symbols[token_value].flags |= USED_SFLAG;
268         return(symbols[token_value].value);
269     }
270
271     if ((token_type == SYMBOL_TT) && (symbols[token_value].flags & UNKNOWN_SFLAG))
272     {   assign_symbol(token_value, next_label, LABEL_T);
273         define_symbol_label(token_value);
274         next_label++;
275         symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG;
276         return(symbols[token_value].value);
277     }
278
279     ebf_curtoken_error("label name");
280     return 0;
281 }
282
283 static void parse_print_z(int finally_return)
284 {   int count = 0; assembly_operand AO;
285
286     /*  print <printlist> -------------------------------------------------- */
287     /*  print_ret <printlist> ---------------------------------------------- */
288     /*  <literal-string> --------------------------------------------------- */
289     /*                                                                       */
290     /*  <printlist> is a comma-separated list of items:                      */
291     /*                                                                       */
292     /*       <literal-string>                                                */
293     /*       <other-expression>                                              */
294     /*       (char) <expression>                                             */
295     /*       (address) <expression>                                          */
296     /*       (string) <expression>                                           */
297     /*       (a) <expression>                                                */
298     /*       (the) <expression>                                              */
299     /*       (The) <expression>                                              */
300     /*       (name) <expression>                                             */
301     /*       (number) <expression>                                           */
302     /*       (property) <expression>                                         */
303     /*       (<routine>) <expression>                                        */
304     /*       (object) <expression>     (for use in low-level code only)      */
305     /* --------------------------------------------------------------------- */
306
307     do
308     {   AI.text = token_text;
309         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
310         switch(token_type)
311         {   case DQ_TT:
312               if (token_text[0] == '^' && token_text[1] == '\0') {
313                   /* The string "^" is always a simple newline. */
314                   assemblez_0(new_line_zc);
315                   break;
316               }
317               if ((int)strlen(token_text) > ZCODE_MAX_INLINE_STRING)
318               {   INITAOT(&AO, LONG_CONSTANT_OT);
319                   AO.marker = STRING_MV;
320                   AO.value  = compile_string(token_text, STRCTX_GAME);
321                   assemblez_1(print_paddr_zc, AO);
322                   if (finally_return)
323                   {   get_next_token();
324                       if ((token_type == SEP_TT)
325                           && (token_value == SEMICOLON_SEP))
326                       {   assemblez_0(new_line_zc);
327                           assemblez_0(rtrue_zc);
328                           return;
329                       }
330                       put_token_back();
331                   }
332                   break;
333               }
334               if (finally_return)
335               {   get_next_token();
336                   if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
337                   {   assemblez_0(print_ret_zc); return;
338                   }
339                   put_token_back();
340               }
341               assemblez_0(print_zc);
342               break;
343
344             case SEP_TT:
345               if (token_value == OPENB_SEP)
346               {   misc_keywords.enabled = TRUE;
347                   get_next_token();
348                   get_next_token();
349                   if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
350                   {   assembly_operand AO1;
351
352                       put_token_back(); put_token_back();
353                       local_variables.enabled = FALSE;
354                       get_next_token();
355                       misc_keywords.enabled = FALSE;
356                       local_variables.enabled = TRUE;
357
358                       if ((token_type == STATEMENT_TT)
359                           &&(token_value == STRING_CODE))
360                       {   token_type = MISC_KEYWORD_TT;
361                           token_value = STRING_MK;
362                       }
363
364                       switch(token_type)
365                       {
366                         case MISC_KEYWORD_TT:
367                           switch(token_value)
368                           {   case CHAR_MK:
369                                   if (runtime_error_checking_switch)
370                                   {   AO = veneer_routine(RT__ChPrintC_VR);
371                                       goto PrintByRoutine;
372                                   }
373                                   get_next_token();
374                                   AO1 = code_generate(
375                                       parse_expression(QUANTITY_CONTEXT),
376                                       QUANTITY_CONTEXT, -1);
377                                   assemblez_1(print_char_zc, AO1);
378                                   goto PrintTermDone;
379                               case ADDRESS_MK:
380                                   if (runtime_error_checking_switch)
381                                   {   AO = veneer_routine(RT__ChPrintA_VR);
382                                       goto PrintByRoutine;
383                                   }
384                                   get_next_token();
385                                   AO1 = code_generate(
386                                       parse_expression(QUANTITY_CONTEXT),
387                                       QUANTITY_CONTEXT, -1);
388                                   assemblez_1(print_addr_zc, AO1);
389                                   goto PrintTermDone;
390                               case STRING_MK:
391                                   if (runtime_error_checking_switch)
392                                   {   AO = veneer_routine(RT__ChPrintS_VR);
393                                       goto PrintByRoutine;
394                                   }
395                                   get_next_token();
396                                   AO1 = code_generate(
397                                       parse_expression(QUANTITY_CONTEXT),
398                                       QUANTITY_CONTEXT, -1);
399                                   assemblez_1(print_paddr_zc, AO1);
400                                   goto PrintTermDone;
401                               case OBJECT_MK:
402                                   if (runtime_error_checking_switch)
403                                   {   AO = veneer_routine(RT__ChPrintO_VR);
404                                       goto PrintByRoutine;
405                                   }
406                                   get_next_token();
407                                   AO1 = code_generate(
408                                       parse_expression(QUANTITY_CONTEXT),
409                                       QUANTITY_CONTEXT, -1);
410                                   assemblez_1(print_obj_zc, AO1);
411                                   goto PrintTermDone;
412                               case THE_MK:
413                                   AO = veneer_routine(DefArt_VR);
414                                   goto PrintByRoutine;
415                               case AN_MK:
416                               case A_MK:
417                                   AO = veneer_routine(InDefArt_VR);
418                                   goto PrintByRoutine;
419                               case CAP_THE_MK:
420                                   AO = veneer_routine(CDefArt_VR);
421                                   goto PrintByRoutine;
422                               case CAP_A_MK:
423                                   AO = veneer_routine(CInDefArt_VR);
424                                   goto PrintByRoutine;
425                               case NAME_MK:
426                                   AO = veneer_routine(PrintShortName_VR);
427                                   goto PrintByRoutine;
428                               case NUMBER_MK:
429                                   AO = veneer_routine(EnglishNumber_VR);
430                                   goto PrintByRoutine;
431                               case PROPERTY_MK:
432                                   AO = veneer_routine(Print__Pname_VR);
433                                   goto PrintByRoutine;
434                               default:
435                error_named("A reserved word was used as a print specification:",
436                                       token_text);
437                           }
438                           break;
439
440                         case SYMBOL_TT:
441                           if (symbols[token_value].flags & UNKNOWN_SFLAG)
442                           {   INITAOT(&AO, LONG_CONSTANT_OT);
443                               AO.value = token_value;
444                               AO.marker = SYMBOL_MV;
445                               AO.symindex = token_value;
446                           }
447                           else
448                           {   INITAOT(&AO, LONG_CONSTANT_OT);
449                               AO.value = symbols[token_value].value;
450                               AO.marker = IROUTINE_MV;
451                               AO.symindex = token_value;
452                               if (symbols[token_value].type != ROUTINE_T)
453                                 ebf_curtoken_error("printing routine name");
454                           }
455                           symbols[token_value].flags |= USED_SFLAG;
456
457                           PrintByRoutine:
458
459                           get_next_token();
460                           if (version_number >= 5)
461                             assemblez_2(call_2n_zc, AO,
462                               code_generate(parse_expression(QUANTITY_CONTEXT),
463                                 QUANTITY_CONTEXT, -1));
464                           else if (version_number == 4)
465                             assemblez_2_to(call_vs_zc, AO,
466                               code_generate(parse_expression(QUANTITY_CONTEXT),
467                                 QUANTITY_CONTEXT, -1), temp_var1);
468                           else
469                             assemblez_2_to(call_zc, AO,
470                               code_generate(parse_expression(QUANTITY_CONTEXT),
471                                 QUANTITY_CONTEXT, -1), temp_var1);
472                           goto PrintTermDone;
473
474                         default: ebf_curtoken_error("print specification");
475                           get_next_token();
476                           assemblez_1(print_num_zc,
477                           code_generate(parse_expression(QUANTITY_CONTEXT),
478                                 QUANTITY_CONTEXT, -1));
479                           goto PrintTermDone;
480                       }
481                   }
482                   put_token_back(); put_token_back(); put_token_back();
483                   misc_keywords.enabled = FALSE;
484                   assemblez_1(print_num_zc,
485                       code_generate(parse_expression(QUANTITY_CONTEXT),
486                           QUANTITY_CONTEXT, -1));
487                   break;
488               }
489
490             default:
491               put_token_back(); misc_keywords.enabled = FALSE;
492               assemblez_1(print_num_zc,
493                   code_generate(parse_expression(QUANTITY_CONTEXT),
494                       QUANTITY_CONTEXT, -1));
495               break;
496         }
497
498         PrintTermDone: misc_keywords.enabled = FALSE;
499
500         count++;
501         get_next_token();
502         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
503         if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
504         {   ebf_curtoken_error("comma");
505             panic_mode_error_recovery(); return;
506         }
507         else get_next_token();
508     } while(TRUE);
509
510     if (count == 0) ebf_curtoken_error("something to print");
511     if (finally_return)
512     {   assemblez_0(new_line_zc);
513         assemblez_0(rtrue_zc);
514     }
515 }
516
517 static void parse_print_g(int finally_return)
518 {   int count = 0; assembly_operand AO, AO2;
519
520     /*  print <printlist> -------------------------------------------------- */
521     /*  print_ret <printlist> ---------------------------------------------- */
522     /*  <literal-string> --------------------------------------------------- */
523     /*                                                                       */
524     /*  <printlist> is a comma-separated list of items:                      */
525     /*                                                                       */
526     /*       <literal-string>                                                */
527     /*       <other-expression>                                              */
528     /*       (char) <expression>                                             */
529     /*       (address) <expression>                                          */
530     /*       (string) <expression>                                           */
531     /*       (a) <expression>                                                */
532     /*       (A) <expression>                                                */
533     /*       (the) <expression>                                              */
534     /*       (The) <expression>                                              */
535     /*       (name) <expression>                                             */
536     /*       (number) <expression>                                           */
537     /*       (property) <expression>                                         */
538     /*       (<routine>) <expression>                                        */
539     /*       (object) <expression>     (for use in low-level code only)      */
540     /* --------------------------------------------------------------------- */
541
542     do
543     {   
544         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
545         switch(token_type)
546         {   case DQ_TT:
547               if (token_text[0] == '^' && token_text[1] == '\0') {
548                   /* The string "^" is always a simple newline. */
549                   INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
550                   assembleg_1(streamchar_gc, AO);
551                   break;
552               }
553               /* We can't compile a string into the instruction,
554                  so this always goes into the string area. */
555               {   INITAOT(&AO, CONSTANT_OT);
556                   AO.marker = STRING_MV;
557                   AO.value  = compile_string(token_text, STRCTX_GAME);
558                   assembleg_1(streamstr_gc, AO);
559                   if (finally_return)
560                   {   get_next_token();
561                       if ((token_type == SEP_TT)
562                           && (token_value == SEMICOLON_SEP))
563                       {   INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
564                           assembleg_1(streamchar_gc, AO); 
565                           INITAOTV(&AO, BYTECONSTANT_OT, 1);
566                           assembleg_1(return_gc, AO); 
567                           return;
568                       }
569                       put_token_back();
570                   }
571                   break;
572               }
573               break;
574
575             case SEP_TT:
576               if (token_value == OPENB_SEP)
577               {   misc_keywords.enabled = TRUE;
578                   get_next_token();
579                   get_next_token();
580                   if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
581                   {   assembly_operand AO1;
582
583                       put_token_back(); put_token_back();
584                       local_variables.enabled = FALSE;
585                       get_next_token();
586                       misc_keywords.enabled = FALSE;
587                       local_variables.enabled = TRUE;
588
589                       if ((token_type == STATEMENT_TT)
590                           &&(token_value == STRING_CODE))
591                       {   token_type = MISC_KEYWORD_TT;
592                           token_value = STRING_MK;
593                       }
594
595                       switch(token_type)
596                       {
597                         case MISC_KEYWORD_TT:
598                           switch(token_value)
599                           {   case CHAR_MK:
600                                   if (runtime_error_checking_switch)
601                                   {   AO = veneer_routine(RT__ChPrintC_VR);
602                                       goto PrintByRoutine;
603                                   }
604                                   get_next_token();
605                                   AO1 = code_generate(
606                                       parse_expression(QUANTITY_CONTEXT),
607                                       QUANTITY_CONTEXT, -1);
608                                   if (is_constant_ot(AO1.type) && AO1.marker == 0) {
609                                       if (AO1.value >= 0 && AO1.value < 0x100)
610                                           assembleg_1(streamchar_gc, AO1);
611                                       else
612                                           assembleg_1(streamunichar_gc, AO1);
613                                   }
614                                   else {
615                                       assembleg_1(streamunichar_gc, AO1);
616                                   }
617                                   goto PrintTermDone;
618                               case ADDRESS_MK:
619                                   if (runtime_error_checking_switch)
620                                       AO = veneer_routine(RT__ChPrintA_VR);
621                                   else
622                                       AO = veneer_routine(Print__Addr_VR);
623                                   goto PrintByRoutine;
624                               case STRING_MK:
625                                   if (runtime_error_checking_switch)
626                                   {   AO = veneer_routine(RT__ChPrintS_VR);
627                                       goto PrintByRoutine;
628                                   }
629                                   get_next_token();
630                                   AO1 = code_generate(
631                                       parse_expression(QUANTITY_CONTEXT),
632                                       QUANTITY_CONTEXT, -1);
633                                   assembleg_1(streamstr_gc, AO1);
634                                   goto PrintTermDone;
635                               case OBJECT_MK:
636                                   if (runtime_error_checking_switch)
637                                   {   AO = veneer_routine(RT__ChPrintO_VR);
638                                       goto PrintByRoutine;
639                                   }
640                                   get_next_token();
641                                   AO1 = code_generate(
642                                       parse_expression(QUANTITY_CONTEXT),
643                                       QUANTITY_CONTEXT, -1);
644                                   INITAOT(&AO2, BYTECONSTANT_OT);
645                                   AO2.value = GOBJFIELD_NAME();
646                                   assembleg_3(aload_gc, AO1, AO2, 
647                                     stack_pointer);
648                                   assembleg_1(streamstr_gc, stack_pointer);
649                                   goto PrintTermDone;
650                               case THE_MK:
651                                   AO = veneer_routine(DefArt_VR);
652                                   goto PrintByRoutine;
653                               case AN_MK:
654                               case A_MK:
655                                   AO = veneer_routine(InDefArt_VR);
656                                   goto PrintByRoutine;
657                               case CAP_THE_MK:
658                                   AO = veneer_routine(CDefArt_VR);
659                                   goto PrintByRoutine;
660                               case CAP_A_MK:
661                                   AO = veneer_routine(CInDefArt_VR);
662                                   goto PrintByRoutine;
663                               case NAME_MK:
664                                   AO = veneer_routine(PrintShortName_VR);
665                                   goto PrintByRoutine;
666                               case NUMBER_MK:
667                                   AO = veneer_routine(EnglishNumber_VR);
668                                   goto PrintByRoutine;
669                               case PROPERTY_MK:
670                                   AO = veneer_routine(Print__Pname_VR);
671                                   goto PrintByRoutine;
672                               default:
673                error_named("A reserved word was used as a print specification:",
674                                       token_text);
675                           }
676                           break;
677
678                         case SYMBOL_TT:
679                           if (symbols[token_value].flags & UNKNOWN_SFLAG)
680                           {   INITAOT(&AO, CONSTANT_OT);
681                               AO.value = token_value;
682                               AO.marker = SYMBOL_MV;
683                               AO.symindex = token_value;
684                           }
685                           else
686                           {   INITAOT(&AO, CONSTANT_OT);
687                               AO.value = symbols[token_value].value;
688                               AO.marker = IROUTINE_MV;
689                               AO.symindex = token_value;
690                               if (symbols[token_value].type != ROUTINE_T)
691                                 ebf_curtoken_error("printing routine name");
692                           }
693                           symbols[token_value].flags |= USED_SFLAG;
694
695                           PrintByRoutine:
696
697                           get_next_token();
698                           INITAOT(&AO2, ZEROCONSTANT_OT);
699                           assembleg_call_1(AO,
700                             code_generate(parse_expression(QUANTITY_CONTEXT),
701                               QUANTITY_CONTEXT, -1),
702                             AO2);
703                           goto PrintTermDone;
704
705                         default: ebf_curtoken_error("print specification");
706                           get_next_token();
707                           assembleg_1(streamnum_gc,
708                           code_generate(parse_expression(QUANTITY_CONTEXT),
709                                 QUANTITY_CONTEXT, -1));
710                           goto PrintTermDone;
711                       }
712                   }
713                   put_token_back(); put_token_back(); put_token_back();
714                   misc_keywords.enabled = FALSE;
715                   assembleg_1(streamnum_gc,
716                       code_generate(parse_expression(QUANTITY_CONTEXT),
717                           QUANTITY_CONTEXT, -1));
718                   break;
719               }
720
721             default:
722               put_token_back(); misc_keywords.enabled = FALSE;
723               assembleg_1(streamnum_gc,
724                   code_generate(parse_expression(QUANTITY_CONTEXT),
725                       QUANTITY_CONTEXT, -1));
726               break;
727         }
728
729         PrintTermDone: misc_keywords.enabled = FALSE;
730
731         count++;
732         get_next_token();
733         if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
734         if ((token_type != SEP_TT) || (token_value != COMMA_SEP))
735         {   ebf_curtoken_error("comma");
736             panic_mode_error_recovery(); return;
737         }
738         else get_next_token();
739     } while(TRUE);
740
741     if (count == 0) ebf_curtoken_error("something to print");
742     if (finally_return)
743     {
744         INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
745         assembleg_1(streamchar_gc, AO); 
746         INITAOTV(&AO, BYTECONSTANT_OT, 1);
747         assembleg_1(return_gc, AO); 
748     }
749 }
750
751 /* Parse any number of ".Label;" lines before a statement.
752    Returns whether a statement can in fact follow. */
753 static int parse_named_label_statements()
754 {
755     while ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
756     {   /*  That is, a full stop, signifying a label  */
757
758         get_next_token();
759         if (token_type != SYMBOL_TT)
760         {
761             ebf_curtoken_error("label name");
762             return TRUE;
763         }
764
765         if (symbols[token_value].flags & UNKNOWN_SFLAG)
766         {   assign_symbol(token_value, next_label, LABEL_T);
767             symbols[token_value].flags |= USED_SFLAG;
768             assemble_label_no(next_label);
769             define_symbol_label(token_value);
770             next_label++;
771         }
772         else
773         {   if (symbols[token_value].type != LABEL_T) {
774                 ebf_curtoken_error("label name");
775                 return TRUE;
776             }
777             if (symbols[token_value].flags & CHANGE_SFLAG)
778             {   symbols[token_value].flags &= (~(CHANGE_SFLAG));
779                 assemble_label_no(symbols[token_value].value);
780                 define_symbol_label(token_value);
781             }
782             else error_named("Duplicate definition of label:", token_text);
783         }
784
785         get_next_token();
786         if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
787         {   ebf_curtoken_error("';'");
788             put_token_back(); return FALSE;
789         }
790
791         /*  Interesting point of Inform grammar: a statement can only
792             consist solely of a label when it is immediately followed
793             by a "}".                                                    */
794
795         get_next_token();
796         if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
797         {   put_token_back(); return FALSE;
798         }
799         /* The following line prevents labels from influencing the positions
800            of sequence points. */
801         statement_debug_location = get_token_location();
802         
803         /* Another label might follow */
804     }
805
806     /* On with the statement */
807     return TRUE;
808 }
809
810 static void parse_statement_z(int break_label, int continue_label)
811 {   int ln, ln2, ln3, ln4, flag;
812     int pre_unreach, labelexists;
813     assembly_operand AO, AO2, AO3, AO4;
814     debug_location spare_debug_location1, spare_debug_location2;
815
816     ASSERT_ZCODE();
817
818     if ((token_type == SEP_TT) && (token_value == HASH_SEP))
819     {   parse_directive(TRUE);
820         parse_statement(break_label, continue_label); return;
821     }
822
823     if ((token_type == SEP_TT) && (token_value == AT_SEP))
824     {   parse_assembly(); return;
825     }
826
827     if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
828
829     if (token_type == DQ_TT)
830     {   parse_print_z(TRUE); return;
831     }
832
833     if ((token_type == SEP_TT) && (token_value == LESS_SEP))
834     {   parse_action(); goto StatementTerminator; }
835
836     if (token_type == EOF_TT)
837     {   ebf_curtoken_error("statement"); return; }
838
839     /* If we don't see a keyword, this must be a function call or
840        other expression-with-side-effects. */
841     if (token_type != STATEMENT_TT)
842     {   put_token_back();
843         AO = parse_expression(VOID_CONTEXT);
844         code_generate(AO, VOID_CONTEXT, -1);
845         if (vivc_flag) { panic_mode_error_recovery(); return; }
846         goto StatementTerminator;
847     }
848
849     statements.enabled = FALSE;
850
851     switch(token_value)
852     {
853     /*  -------------------------------------------------------------------- */
854     /*  box <string-1> ... <string-n> -------------------------------------- */
855     /*  -------------------------------------------------------------------- */
856
857         case BOX_CODE:
858              if (version_number == 3)
859                  warning("The 'box' statement has no effect in a version 3 game");
860              INITAOT(&AO3, LONG_CONSTANT_OT);
861                  AO3.value = begin_table_array();
862                  AO3.marker = ARRAY_MV;
863                  ln = 0; ln2 = 0;
864                  do
865                  {   get_next_token();
866                      if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
867                          break;
868                      if (token_type != DQ_TT)
869                          ebf_curtoken_error("text of box line in double-quotes");
870                      {   int i, j;
871                          for (i=0, j=0; token_text[i] != 0; j++)
872                              if (token_text[i] == '@')
873                              {   if (token_text[i+1] == '@')
874                                  {   i = i + 2;
875                                      while (isdigit(token_text[i])) i++;
876                                  }
877                                  else
878                                  {   i++;
879                                      if (token_text[i] != 0) i++;
880                                      if (token_text[i] != 0) i++;
881                                  }
882                              }
883                              else i++;
884                          if (j > ln2) ln2 = j;
885                      }
886                      put_token_back();
887                      array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
888                  } while (TRUE);
889                  finish_array(ln, FALSE);
890                  if (ln == 0)
891                      error("No lines of text given for 'box' display");
892
893                  if (version_number == 3) return;
894
895                  INITAOTV(&AO2, SHORT_CONSTANT_OT, ln2);
896                  INITAOTV(&AO4, VARIABLE_OT, 255);
897                  assemblez_3_to(call_vs_zc, veneer_routine(Box__Routine_VR),
898                      AO2, AO3, AO4);
899                  return;
900
901     /*  -------------------------------------------------------------------- */
902     /*  break -------------------------------------------------------------- */
903     /*  -------------------------------------------------------------------- */
904
905         case BREAK_CODE:
906                  if (break_label == -1)
907                  error("'break' can only be used in a loop or 'switch' block");
908                  else
909                      assemblez_jump(break_label);
910                  break;
911
912     /*  -------------------------------------------------------------------- */
913     /*  continue ----------------------------------------------------------- */
914     /*  -------------------------------------------------------------------- */
915
916         case CONTINUE_CODE:
917                  if (continue_label == -1)
918                  error("'continue' can only be used in a loop block");
919                  else
920                      assemblez_jump(continue_label);
921                  break;
922
923     /*  -------------------------------------------------------------------- */
924     /*  do <codeblock> until (<condition>) --------------------------------- */
925     /*  -------------------------------------------------------------------- */
926
927         case DO_CODE:
928                  assemble_label_no(ln = next_label++);
929                  ln2 = next_label++; ln3 = next_label++;
930                  parse_code_block(ln3, ln2, 0);
931                  statements.enabled = TRUE;
932                  get_next_token();
933                  if ((token_type == STATEMENT_TT)
934                      && (token_value == UNTIL_CODE))
935                  {   assemble_forward_label_no(ln2);
936                      match_open_bracket();
937                      AO = parse_expression(CONDITION_CONTEXT);
938                      match_close_bracket();
939                      code_generate(AO, CONDITION_CONTEXT, ln);
940                  }
941                  else error("'do' without matching 'until'");
942
943                  assemble_forward_label_no(ln3);
944                  break;
945
946     /*  -------------------------------------------------------------------- */
947     /*  font on/off -------------------------------------------------------- */
948     /*  -------------------------------------------------------------------- */
949
950         case FONT_CODE:
951                  misc_keywords.enabled = TRUE;
952                  get_next_token();
953                  misc_keywords.enabled = FALSE;
954                  if ((token_type != MISC_KEYWORD_TT)
955                      || ((token_value != ON_MK)
956                          && (token_value != OFF_MK)))
957                  {   ebf_curtoken_error("'on' or 'off'");
958                      panic_mode_error_recovery();
959                      break;
960                  }
961
962                  if (version_number >= 5)
963                  {   /* Use the V5 @set_font opcode, setting font 4
964                         (for font off) or 1 (for font on). */
965                      INITAOT(&AO, SHORT_CONSTANT_OT);
966                      if (token_value == ON_MK)
967                          AO.value = 1;
968                      else
969                          AO.value = 4;
970                      assemblez_1_to(set_font_zc, AO, temp_var1);
971                      break;
972                  }
973
974                  /* Set the fixed-pitch header bit. */
975                  INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
976                  INITAOTV(&AO2, SHORT_CONSTANT_OT, 8);
977                  INITAOTV(&AO3, VARIABLE_OT, 255);
978                  assemblez_2_to(loadw_zc, AO, AO2, AO3);
979
980                  if (token_value == ON_MK)
981                  {   INITAOTV(&AO4, LONG_CONSTANT_OT, 0xfffd);
982                      assemblez_2_to(and_zc, AO4, AO3, AO3);
983                  }
984                  else
985                  {   INITAOTV(&AO4, SHORT_CONSTANT_OT, 2);
986                      assemblez_2_to(or_zc, AO4, AO3, AO3);
987                  }
988
989                  assemblez_3(storew_zc, AO, AO2, AO3);
990                  break;
991
992     /*  -------------------------------------------------------------------- */
993     /*  for (<initialisation> : <continue-condition> : <updating>) --------- */
994     /*  -------------------------------------------------------------------- */
995
996         /*  Note that it's legal for any or all of the three sections of a
997             'for' specification to be empty.  This 'for' implementation
998             often wastes 3 bytes with a redundant branch rather than keep
999             expression parse trees for long periods (as previous versions
1000             of Inform did, somewhat crudely by simply storing the textual
1001             form of a 'for' loop).  It is adequate for now.                  */
1002
1003         case FOR_CODE:
1004                  match_open_bracket();
1005                  get_next_token();
1006
1007                  /*  Initialisation code  */
1008                  AO.type = OMITTED_OT;
1009                  spare_debug_location1 = statement_debug_location;
1010                  AO2.type = OMITTED_OT; flag = 0;
1011                  spare_debug_location2 = statement_debug_location;
1012
1013                  if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1014                  {   put_token_back();
1015                      if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
1016                      {   sequence_point_follows = TRUE;
1017                          statement_debug_location = get_token_location();
1018                          code_generate(parse_expression(FORINIT_CONTEXT),
1019                              VOID_CONTEXT, -1);
1020                      }
1021                      get_next_token();
1022                      if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
1023                      {   get_next_token();
1024                          if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
1025                          {   assemble_label_no(ln = next_label++);
1026                              ln2 = next_label++;
1027                              parse_code_block(ln2, ln, 0);
1028                              sequence_point_follows = FALSE;
1029                              if (!execution_never_reaches_here)
1030                                  assemblez_jump(ln);
1031                              assemble_forward_label_no(ln2);
1032                              return;
1033                          }
1034                          goto ParseUpdate;
1035                      }
1036                      put_token_back();
1037                      if (!match_colon()) break;
1038                  }
1039
1040                  get_next_token();
1041                  if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1042                  {   put_token_back();
1043                      spare_debug_location1 = get_token_location();
1044                      AO = parse_expression(CONDITION_CONTEXT);
1045                      if (!match_colon()) break;
1046                  }
1047                  get_next_token();
1048
1049                  ParseUpdate:
1050                  if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
1051                  {   put_token_back();
1052                      spare_debug_location2 = get_token_location();
1053                      AO2 = parse_expression(VOID_CONTEXT);
1054                      match_close_bracket();
1055                      flag = test_for_incdec(AO2);
1056                  }
1057
1058                  ln = next_label++;
1059                  ln2 = next_label++;
1060                  ln3 = next_label++;
1061
1062                  if ((AO2.type == OMITTED_OT) || (flag != 0))
1063                  {
1064                      assemble_label_no(ln);
1065                      if (flag==0) assemble_label_no(ln2);
1066
1067                      /*  The "finished yet?" condition  */
1068
1069                      if (AO.type != OMITTED_OT)
1070                      {   sequence_point_follows = TRUE;
1071                          statement_debug_location = spare_debug_location1;
1072                          code_generate(AO, CONDITION_CONTEXT, ln3);
1073                      }
1074
1075                  }
1076                  else
1077                  {
1078                      /*  This is the jump which could be avoided with the aid
1079                          of long-term expression storage  */
1080
1081                      sequence_point_follows = FALSE;
1082                      assemblez_jump(ln2);
1083
1084                      /*  The "update" part  */
1085
1086                      assemble_label_no(ln);
1087                      sequence_point_follows = TRUE;
1088                      statement_debug_location = spare_debug_location2;
1089                      code_generate(AO2, VOID_CONTEXT, -1);
1090
1091                      assemble_label_no(ln2);
1092
1093                      /*  The "finished yet?" condition  */
1094
1095                      if (AO.type != OMITTED_OT)
1096                      {   sequence_point_follows = TRUE;
1097                          statement_debug_location = spare_debug_location1;
1098                          code_generate(AO, CONDITION_CONTEXT, ln3);
1099                      }
1100                  }
1101
1102                  if (flag != 0)
1103                  {
1104                      /*  In this optimised case, update code is at the end
1105                          of the loop block, so "continue" goes there  */
1106
1107                      parse_code_block(ln3, ln2, 0);
1108                      assemble_label_no(ln2);
1109
1110                      sequence_point_follows = TRUE;
1111                      statement_debug_location = spare_debug_location2;
1112                      if (flag > 0)
1113                      {   INITAOTV(&AO3, SHORT_CONSTANT_OT, flag);
1114                          assemblez_1(inc_zc, AO3);
1115                      }
1116                      else
1117                      {   INITAOTV(&AO3, SHORT_CONSTANT_OT, -flag);
1118                          assemblez_1(dec_zc, AO3);
1119                      }
1120                      assemblez_jump(ln);
1121                  }
1122                  else
1123                  {
1124                      /*  In the unoptimised case, update code is at the
1125                          start of the loop block, so "continue" goes there  */
1126
1127                      parse_code_block(ln3, ln, 0);
1128                      if (!execution_never_reaches_here)
1129                      {   sequence_point_follows = FALSE;
1130                          assemblez_jump(ln);
1131                      }
1132                  }
1133
1134                  assemble_forward_label_no(ln3);
1135                  return;
1136
1137     /*  -------------------------------------------------------------------- */
1138     /*  give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
1139     /*  -------------------------------------------------------------------- */
1140
1141         case GIVE_CODE:
1142                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1143                           QUANTITY_CONTEXT, -1);
1144                  check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
1145                  if ((AO.type == VARIABLE_OT) && (AO.value == 0))
1146                  {   INITAOTV(&AO, SHORT_CONSTANT_OT, 252);
1147                      if (version_number != 6) assemblez_1(pull_zc, AO);
1148                      else assemblez_0_to(pull_zc, AO);
1149                      AO.type = VARIABLE_OT;
1150                  }
1151
1152                  do
1153                  {   get_next_token();
1154                      if ((token_type == SEP_TT)&&(token_value == SEMICOLON_SEP))
1155                          return;
1156                      if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
1157                          ln = clear_attr_zc;
1158                      else
1159                      {   ln = set_attr_zc;
1160                          put_token_back();
1161                      }
1162                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1163                                QUANTITY_CONTEXT, -1);
1164                      check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
1165                      if (runtime_error_checking_switch)
1166                      {   ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
1167                          if (version_number >= 5)
1168                              assemblez_3(call_vn_zc, veneer_routine(ln2),
1169                              AO, AO2);
1170                          else
1171                          {   
1172                              assemblez_3_to(call_zc, veneer_routine(ln2),
1173                                  AO, AO2, temp_var1);
1174                          }
1175                      }
1176                      else
1177                          assemblez_2(ln, AO, AO2);
1178                  } while(TRUE);
1179
1180     /*  -------------------------------------------------------------------- */
1181     /*  if (<condition>) <codeblock> [else <codeblock>] -------------------- */
1182     /*  -------------------------------------------------------------------- */
1183
1184         case IF_CODE:
1185                  flag = FALSE; /* set if there's an "else" */
1186                  ln2 = 0;
1187                  pre_unreach = execution_never_reaches_here;
1188
1189                  match_open_bracket();
1190                  AO = parse_expression(CONDITION_CONTEXT);
1191                  match_close_bracket();
1192
1193                  statements.enabled = TRUE;
1194                  get_next_token();
1195                  if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
1196                      ln = -4;
1197                  else
1198                  if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
1199                      ln = -3;
1200                  else
1201                  {   put_token_back();
1202                      ln = next_label++;
1203                  }
1204
1205                  /* The condition */
1206                  code_generate(AO, CONDITION_CONTEXT, ln);
1207
1208                  if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
1209                      /* If the condition never falls through to here, then
1210                         it was an "if (0)" test. Our convention is to skip
1211                         the "not reached" warnings for this case. */
1212                      execution_never_reaches_here |= EXECSTATE_NOWARN;
1213                  }
1214
1215                  /* The "if" block */
1216                  if (ln >= 0) parse_code_block(break_label, continue_label, 0);
1217                  else
1218                  {   get_next_token();
1219                      if ((token_type != SEP_TT)
1220                          || (token_value != SEMICOLON_SEP))
1221                      {   ebf_curtoken_error("';'");
1222                          put_token_back();
1223                      }
1224                  }
1225
1226                  statements.enabled = TRUE;
1227                  get_next_token();
1228
1229                  /* An #if directive around the ELSE clause is legal. */
1230                  while ((token_type == SEP_TT) && (token_value == HASH_SEP))
1231                  {   parse_directive(TRUE);
1232                      statements.enabled = TRUE;
1233                      get_next_token();
1234                  }
1235                  
1236                  if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
1237                  {   flag = TRUE;
1238                      if (ln >= 0)
1239                      {   ln2 = next_label++;
1240                          if (!execution_never_reaches_here)
1241                          {   sequence_point_follows = FALSE;
1242                              assemblez_jump(ln2);
1243                          }
1244                      }
1245                  }
1246                  else put_token_back();
1247
1248                  /* The "else" label (or end of statement, if there is no "else") */
1249                  labelexists = FALSE;
1250                  if (ln >= 0) labelexists = assemble_forward_label_no(ln);
1251
1252                  if (flag)
1253                  {
1254                      /* If labelexists is false, then we started with
1255                         "if (1)". In this case, we don't want a "not
1256                         reached" warning on the "else" block. We
1257                         temporarily disable the NOWARN flag, and restore it
1258                         afterwards. */
1259                      int saved_unreach = 0;
1260                      if (execution_never_reaches_here && !labelexists) {
1261                          saved_unreach = execution_never_reaches_here;
1262                          execution_never_reaches_here |= EXECSTATE_NOWARN;
1263                      }
1264
1265                      /* The "else" block */
1266                      parse_code_block(break_label, continue_label, 0);
1267
1268                      if (execution_never_reaches_here && !labelexists) {
1269                          if (saved_unreach & EXECSTATE_NOWARN)
1270                              execution_never_reaches_here |= EXECSTATE_NOWARN;
1271                          else
1272                              execution_never_reaches_here &= ~EXECSTATE_NOWARN;
1273                      }
1274
1275                      /* The post-"else" label */
1276                      if (ln >= 0) assemble_forward_label_no(ln2);
1277                  }
1278                  else
1279                  {
1280                      /* There was no "else". If we're unreachable, then the
1281                         statement returned unconditionally, which means 
1282                         "if (1) return". Skip warnings. */
1283                      if (!pre_unreach && execution_never_reaches_here) {
1284                          execution_never_reaches_here |= EXECSTATE_NOWARN;
1285                      }
1286                  }
1287                          
1288                  return;
1289
1290     /*  -------------------------------------------------------------------- */
1291     /*  inversion ---------------------------------------------------------- */
1292     /*  -------------------------------------------------------------------- */
1293
1294         case INVERSION_CODE:
1295                  INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
1296                  INITAOT(&AO2, SHORT_CONSTANT_OT);
1297
1298                  AO2.value  = 60;
1299                  assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1300                  assemblez_1(print_char_zc, temp_var1);
1301                  AO2.value  = 61;
1302                  assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1303                  assemblez_1(print_char_zc, temp_var1);
1304                  AO2.value  = 62;
1305                  assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1306                  assemblez_1(print_char_zc, temp_var1);
1307                  AO2.value  = 63;
1308                  assemblez_2_to(loadb_zc, AO, AO2, temp_var1);
1309                  assemblez_1(print_char_zc, temp_var1);
1310                  break;
1311
1312     /*  -------------------------------------------------------------------- */
1313     /*  jump <label> ------------------------------------------------------- */
1314     /*  -------------------------------------------------------------------- */
1315
1316         case JUMP_CODE:
1317                  assemblez_jump(parse_label());
1318                  break;
1319
1320     /*  -------------------------------------------------------------------- */
1321     /*  move <expression> to <expression> ---------------------------------- */
1322     /*  -------------------------------------------------------------------- */
1323
1324         case MOVE_CODE:
1325                  misc_keywords.enabled = TRUE;
1326                  AO = parse_expression(QUANTITY_CONTEXT);
1327
1328                  get_next_token();
1329                  misc_keywords.enabled = FALSE;
1330                  if ((token_type != MISC_KEYWORD_TT)
1331                      || (token_value != TO_MK))
1332                  {   ebf_curtoken_error("'to'");
1333                      panic_mode_error_recovery();
1334                      return;
1335                  }
1336
1337                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1338                      QUANTITY_CONTEXT, -1);
1339                  AO = code_generate(AO, QUANTITY_CONTEXT, -1);
1340                  check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
1341                  check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
1342                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
1343                  {   if (version_number >= 5)
1344                          assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
1345                              AO, AO2);
1346                      else
1347                      {   assemblez_3_to(call_zc, veneer_routine(RT__ChT_VR),
1348                              AO, AO2, temp_var1);
1349                      }
1350                  }
1351                  else
1352                      assemblez_2(insert_obj_zc, AO, AO2);
1353                  break;
1354
1355     /*  -------------------------------------------------------------------- */
1356     /*  new_line ----------------------------------------------------------- */
1357     /*  -------------------------------------------------------------------- */
1358
1359         case NEW_LINE_CODE:  assemblez_0(new_line_zc); break;
1360
1361     /*  -------------------------------------------------------------------- */
1362     /*  objectloop (<initialisation>) <codeblock> -------------------------- */
1363     /*  -------------------------------------------------------------------- */
1364
1365         case OBJECTLOOP_CODE:
1366
1367                  match_open_bracket();
1368                  get_next_token();
1369                  INITAOT(&AO, VARIABLE_OT);
1370                  if (token_type == LOCAL_VARIABLE_TT)
1371                      AO.value = token_value;
1372                  else
1373                  if ((token_type == SYMBOL_TT) &&
1374                      (symbols[token_value].type == GLOBAL_VARIABLE_T))
1375                      AO.value = symbols[token_value].value;
1376                  else
1377                  {   ebf_curtoken_error("'objectloop' variable");
1378                      panic_mode_error_recovery(); break;
1379                  }
1380                  misc_keywords.enabled = TRUE;
1381                  get_next_token(); flag = TRUE;
1382                  misc_keywords.enabled = FALSE;
1383                  if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
1384                      flag = FALSE;
1385
1386                  ln = 0;
1387                  if ((token_type == MISC_KEYWORD_TT)
1388                      && (token_value == NEAR_MK)) ln = 1;
1389                  if ((token_type == MISC_KEYWORD_TT)
1390                      && (token_value == FROM_MK)) ln = 2;
1391                  if ((token_type == CND_TT) && (token_value == IN_COND))
1392                  {   get_next_token();
1393                      get_next_token();
1394                      if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
1395                          ln = 3;
1396                      put_token_back();
1397                      put_token_back();
1398                  }
1399
1400                  if (ln > 0)
1401                  {   /*  Old style (Inform 5) objectloops: note that we
1402                          implement objectloop (a in b) in the old way since
1403                          this runs through objects in a different order from
1404                          the new way, and there may be existing Inform code
1405                          relying on this.                                    */
1406                      assembly_operand AO4;
1407                      INITAO(&AO4);
1408
1409                      sequence_point_follows = TRUE;
1410                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1411                          QUANTITY_CONTEXT, -1);
1412                      match_close_bracket();
1413                      if (ln == 1)
1414                      {   INITAOTV(&AO3, VARIABLE_OT, 0);
1415                          if (runtime_error_checking_switch)
1416                                  AO2 = check_nonzero_at_runtime(AO2, -1,
1417                                      OBJECTLOOP_RTE);
1418                          assemblez_1_to(get_parent_zc, AO2, AO3);
1419                          assemblez_objcode(get_child_zc, AO3, AO3, -2, TRUE);
1420                          AO2 = AO3;
1421                      }
1422                      if (ln == 3)
1423                      {   INITAOTV(&AO3, VARIABLE_OT, 0);
1424                          if (runtime_error_checking_switch)
1425                          {   AO4 = AO2;
1426                              AO2 = check_nonzero_at_runtime(AO2, -1,
1427                                  CHILD_RTE);
1428                          }
1429                          assemblez_objcode(get_child_zc, AO2, AO3, -2, TRUE);
1430                          AO2 = AO3;
1431                      }
1432                      assemblez_store(AO, AO2);
1433                      assemblez_1_branch(jz_zc, AO, ln2 = next_label++, TRUE);
1434                      assemble_label_no(ln4 = next_label++);
1435                      parse_code_block(ln2, ln3 = next_label++, 0);
1436                      sequence_point_follows = FALSE;
1437                      assemble_label_no(ln3);
1438                      if (runtime_error_checking_switch)
1439                      {   AO2 = check_nonzero_at_runtime(AO, ln2,
1440                               OBJECTLOOP2_RTE);
1441                          if ((ln == 3)
1442                              && ((AO4.type != VARIABLE_OT)||(AO4.value != 0))
1443                              && ((AO4.type != VARIABLE_OT)
1444                                  ||(AO4.value != AO.value)))
1445                          {   assembly_operand en_ao;
1446                              INITAOTV(&en_ao, SHORT_CONSTANT_OT, OBJECTLOOP_BROKEN_RTE);
1447                              assemblez_2_branch(jin_zc, AO, AO4,
1448                                  next_label, TRUE);
1449                              assemblez_3(call_vn_zc, veneer_routine(RT__Err_VR),
1450                                  en_ao, AO);
1451                              assemblez_jump(ln2);
1452                              assemble_label_no(next_label++);
1453                          }
1454                      }
1455                      else AO2 = AO;
1456                      assemblez_objcode(get_sibling_zc, AO2, AO, ln4, TRUE);
1457                      assemble_label_no(ln2);
1458                      return;
1459                  }
1460
1461                  sequence_point_follows = TRUE;
1462                  INITAOTV(&AO2, SHORT_CONSTANT_OT, 1);
1463                  assemblez_store(AO, AO2);
1464
1465                  assemble_label_no(ln = next_label++);
1466                  ln2 = next_label++;
1467                  ln3 = next_label++;
1468                  if (flag)
1469                  {   put_token_back();
1470                      put_token_back();
1471                      sequence_point_follows = TRUE;
1472                      code_generate(parse_expression(CONDITION_CONTEXT),
1473                          CONDITION_CONTEXT, ln3);
1474                      match_close_bracket();
1475                  }
1476                  parse_code_block(ln2, ln3, 0);
1477
1478                  sequence_point_follows = FALSE;
1479                  assemble_label_no(ln3);
1480                  assemblez_inc(AO);
1481                  INITAOTV(&AO2, LONG_CONSTANT_OT, no_objects);
1482                  AO2.marker = NO_OBJS_MV;
1483                  assemblez_2_branch(jg_zc, AO, AO2, ln2, TRUE);
1484                  assemblez_jump(ln);
1485                  assemble_label_no(ln2);
1486                  return;
1487
1488     /*  -------------------------------------------------------------------- */
1489     /*  (see routine above) ------------------------------------------------ */
1490     /*  -------------------------------------------------------------------- */
1491
1492         case PRINT_CODE:
1493             get_next_token();
1494             parse_print_z(FALSE); return;
1495         case PRINT_RET_CODE:
1496             get_next_token();
1497             parse_print_z(TRUE); return;
1498
1499     /*  -------------------------------------------------------------------- */
1500     /*  quit --------------------------------------------------------------- */
1501     /*  -------------------------------------------------------------------- */
1502
1503         case QUIT_CODE:      assemblez_0(quit_zc); break;
1504
1505     /*  -------------------------------------------------------------------- */
1506     /*  read <expression> <expression> [<Routine>] ------------------------- */
1507     /*  -------------------------------------------------------------------- */
1508
1509         case READ_CODE:
1510                  INITAOTV(&AO, VARIABLE_OT, 252);
1511                  assemblez_store(AO,
1512                      code_generate(parse_expression(QUANTITY_CONTEXT),
1513                                    QUANTITY_CONTEXT, -1));
1514                  if (version_number > 3)
1515                  {   INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
1516                      INITAOTV(&AO4, SHORT_CONSTANT_OT, 0);
1517                      assemblez_3(storeb_zc, AO, AO3, AO4);
1518                  }
1519                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1520                            QUANTITY_CONTEXT, -1);
1521
1522                  get_next_token();
1523                  if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
1524                      put_token_back();
1525                  else
1526                  {   if (version_number == 3)
1527                          error(
1528 "In Version 3 no status-line drawing routine can be given");
1529                      else
1530                      {   assembly_operand AO5;
1531                          /* Move the temp4 (buffer) value to the stack,
1532                             since the routine might alter temp4. */
1533                          assemblez_store(stack_pointer, AO);
1534                          AO = stack_pointer;
1535                          put_token_back();
1536                          AO5 = parse_expression(CONSTANT_CONTEXT);
1537
1538                          if (version_number >= 5)
1539                              assemblez_1(call_1n_zc, AO5);
1540                          else
1541                              assemblez_1_to(call_zc, AO5, temp_var1);
1542                      }
1543                  }
1544
1545                  if (version_number > 4)
1546                  {   assemblez_2_to(aread_zc, AO, AO2, temp_var1);
1547                  }
1548                  else assemblez_2(sread_zc, AO, AO2);
1549                  break;
1550
1551     /*  -------------------------------------------------------------------- */
1552     /*  remove <expression> ------------------------------------------------ */
1553     /*  -------------------------------------------------------------------- */
1554
1555         case REMOVE_CODE:
1556                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1557                      QUANTITY_CONTEXT, -1);
1558                  check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
1559                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
1560                  {   if (version_number >= 5)
1561                          assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR),
1562                              AO);
1563                      else
1564                      {   assemblez_2_to(call_zc, veneer_routine(RT__ChR_VR),
1565                              AO, temp_var1);
1566                      }
1567                  }
1568                  else
1569                      assemblez_1(remove_obj_zc, AO);
1570                  break;
1571
1572     /*  -------------------------------------------------------------------- */
1573     /*  restore <label> ---------------------------------------------------- */
1574     /*  -------------------------------------------------------------------- */
1575
1576         case RESTORE_CODE:
1577                  if (version_number < 5)
1578                      assemblez_0_branch(restore_zc, parse_label(), TRUE);
1579                  else
1580                  {   INITAOTV(&AO2, SHORT_CONSTANT_OT, 2);
1581                      assemblez_0_to(restore_zc, temp_var1);
1582                      assemblez_2_branch(je_zc, temp_var1, AO2, parse_label(), TRUE);
1583                  }
1584                  break;
1585
1586     /*  -------------------------------------------------------------------- */
1587     /*  return [<expression>] ---------------------------------------------- */
1588     /*  -------------------------------------------------------------------- */
1589
1590         case RETURN_CODE:
1591                  get_next_token();
1592                  if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
1593                  {   assemblez_0(rtrue_zc); return; }
1594                  put_token_back();
1595                  AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
1596                      QUANTITY_CONTEXT, -1);
1597                  if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 0)
1598                      && (AO.marker == 0))
1599                  {   assemblez_0(rfalse_zc); break; }
1600                  if ((AO.type == SHORT_CONSTANT_OT) && (AO.value == 1)
1601                      && (AO.marker == 0))
1602                  {   assemblez_0(rtrue_zc); break; }
1603                  if ((AO.type == VARIABLE_OT) && (AO.value == 0))
1604                  {   assemblez_0(ret_popped_zc); break; }
1605                  assemblez_1(ret_zc, AO);
1606                  break;
1607
1608     /*  -------------------------------------------------------------------- */
1609     /*  rfalse ------------------------------------------------------------- */
1610     /*  -------------------------------------------------------------------- */
1611
1612         case RFALSE_CODE:  assemblez_0(rfalse_zc); break;
1613
1614     /*  -------------------------------------------------------------------- */
1615     /*  rtrue -------------------------------------------------------------- */
1616     /*  -------------------------------------------------------------------- */
1617
1618         case RTRUE_CODE:   assemblez_0(rtrue_zc); break;
1619
1620     /*  -------------------------------------------------------------------- */
1621     /*  save <label> ------------------------------------------------------- */
1622     /*  -------------------------------------------------------------------- */
1623
1624         case SAVE_CODE:
1625                  if (version_number < 5)
1626                      assemblez_0_branch(save_zc, parse_label(), TRUE);
1627                  else
1628                  {   INITAOTV(&AO, VARIABLE_OT, 255);
1629                      assemblez_0_to(save_zc, AO);
1630                      assemblez_1_branch(jz_zc, AO, parse_label(), FALSE);
1631                  }
1632                  break;
1633
1634     /*  -------------------------------------------------------------------- */
1635     /*  spaces <expression> ------------------------------------------------ */
1636     /*  -------------------------------------------------------------------- */
1637
1638         case SPACES_CODE:
1639                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1640                      QUANTITY_CONTEXT, -1);
1641                  INITAOTV(&AO2, VARIABLE_OT, 255);
1642
1643                  assemblez_store(AO2, AO);
1644
1645                  INITAOTV(&AO, SHORT_CONSTANT_OT, 32);
1646                  INITAOTV(&AO3, SHORT_CONSTANT_OT, 1);
1647
1648                  assemblez_2_branch(jl_zc, AO2, AO3, ln = next_label++, TRUE);
1649                  assemble_label_no(ln2 = next_label++);
1650                  assemblez_1(print_char_zc, AO);
1651                  assemblez_dec(AO2);
1652                  assemblez_1_branch(jz_zc, AO2, ln2, FALSE);
1653                  assemble_label_no(ln);
1654                  break;
1655
1656     /*  -------------------------------------------------------------------- */
1657     /*  string <expression> <literal-string> ------------------------------- */
1658     /*  -------------------------------------------------------------------- */
1659
1660         case STRING_CODE:
1661                  INITAOTV(&AO, SHORT_CONSTANT_OT, 0);
1662                  INITAOTV(&AO2, SHORT_CONSTANT_OT, 12);
1663                  INITAOTV(&AO3, VARIABLE_OT, 252);
1664                  assemblez_2_to(loadw_zc, AO, AO2, AO3);
1665                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
1666                      QUANTITY_CONTEXT, -1);
1667                  if (is_constant_ot(AO2.type) && AO2.marker == 0) {
1668                      /* Compile-time check */
1669                      if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
1670                          error_max_dynamic_strings(AO2.value);
1671                          AO2.value = 0;
1672                      }
1673                  }
1674                  get_next_token();
1675                  if (token_type == DQ_TT)
1676                  {   INITAOT(&AO4, LONG_CONSTANT_OT);
1677                      /* This string must be in low memory so that the
1678                         dynamic string table can refer to it. */
1679                      AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
1680                  }
1681                  else
1682                  {   put_token_back();
1683                      AO4 = parse_expression(CONSTANT_CONTEXT);
1684                  }
1685                  assemblez_3(storew_zc, AO3, AO2, AO4);
1686                  break;
1687
1688     /*  -------------------------------------------------------------------- */
1689     /*  style roman/reverse/bold/underline/fixed --------------------------- */
1690     /*  -------------------------------------------------------------------- */
1691
1692         case STYLE_CODE:
1693                  if (version_number==3)
1694                  {   error(
1695 "The 'style' statement cannot be used for Version 3 games");
1696                      panic_mode_error_recovery();
1697                      break;
1698                  }
1699
1700                  misc_keywords.enabled = TRUE;
1701                  get_next_token();
1702                  misc_keywords.enabled = FALSE;
1703                  if ((token_type != MISC_KEYWORD_TT)
1704                      || ((token_value != ROMAN_MK)
1705                          && (token_value != REVERSE_MK)
1706                          && (token_value != BOLD_MK)
1707                          && (token_value != UNDERLINE_MK)
1708                          && (token_value != FIXED_MK)))
1709                  {   ebf_curtoken_error(
1710 "'roman', 'bold', 'underline', 'reverse' or 'fixed'");
1711                      panic_mode_error_recovery();
1712                      break;
1713                  }
1714
1715                  INITAOT(&AO, SHORT_CONSTANT_OT);
1716                  switch(token_value)
1717                  {   case ROMAN_MK: AO.value = 0; break;
1718                      case REVERSE_MK: AO.value = 1; break;
1719                      case BOLD_MK: AO.value = 2; break;
1720                      case UNDERLINE_MK: AO.value = 4; break;
1721                      case FIXED_MK: AO.value = 8; break;
1722                  }
1723                  assemblez_1(set_text_style_zc, AO); break;
1724
1725     /*  -------------------------------------------------------------------- */
1726     /*  switch (<expression>) <codeblock> ---------------------------------- */
1727     /*  -------------------------------------------------------------------- */
1728
1729         case SWITCH_CODE:
1730                  match_open_bracket();
1731                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
1732                      QUANTITY_CONTEXT, -1);
1733                  match_close_bracket();
1734
1735                  INITAOTV(&AO2, VARIABLE_OT, 255);
1736                  assemblez_store(AO2, AO);
1737
1738                  parse_code_block(ln = next_label++, continue_label, 1);
1739                  assemble_forward_label_no(ln);
1740                  return;
1741
1742     /*  -------------------------------------------------------------------- */
1743     /*  while (<condition>) <codeblock> ------------------------------------ */
1744     /*  -------------------------------------------------------------------- */
1745
1746         case WHILE_CODE:
1747                  assemble_label_no(ln = next_label++);
1748                  match_open_bracket();
1749
1750                  code_generate(parse_expression(CONDITION_CONTEXT),
1751                      CONDITION_CONTEXT, ln2 = next_label++);
1752                  match_close_bracket();
1753
1754                  parse_code_block(ln2, ln, 0);
1755                  sequence_point_follows = FALSE;
1756                  assemblez_jump(ln);
1757                  assemble_forward_label_no(ln2);
1758                  return;
1759
1760     /*  -------------------------------------------------------------------- */
1761
1762         case SDEFAULT_CODE:
1763                  error("'default' without matching 'switch'"); break;
1764         case ELSE_CODE:
1765                  error("'else' without matching 'if'"); break;
1766         case UNTIL_CODE:
1767                  error("'until' without matching 'do'");
1768                  panic_mode_error_recovery(); return;
1769     }
1770
1771     StatementTerminator:
1772
1773     get_next_token();
1774     if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
1775     {   ebf_curtoken_error("';'");
1776         put_token_back();
1777     }
1778 }
1779
1780 static void parse_statement_g(int break_label, int continue_label)
1781 {   int ln, ln2, ln3, ln4, flag, onstack;
1782     int pre_unreach, labelexists;
1783     assembly_operand AO, AO2, AO3, AO4;
1784     debug_location spare_debug_location1, spare_debug_location2;
1785
1786     ASSERT_GLULX();
1787
1788     if ((token_type == SEP_TT) && (token_value == HASH_SEP))
1789     {   parse_directive(TRUE);
1790         parse_statement(break_label, continue_label); return;
1791     }
1792
1793     if ((token_type == SEP_TT) && (token_value == AT_SEP))
1794     {   parse_assembly(); return;
1795     }
1796
1797     if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
1798
1799     if (token_type == DQ_TT)
1800     {   parse_print_g(TRUE); return;
1801     }
1802
1803     if ((token_type == SEP_TT) && (token_value == LESS_SEP))
1804     {   parse_action(); goto StatementTerminator; }
1805
1806     if (token_type == EOF_TT)
1807     {   ebf_curtoken_error("statement"); return; }
1808
1809     /* If we don't see a keyword, this must be a function call or
1810        other expression-with-side-effects. */
1811     if (token_type != STATEMENT_TT)
1812     {   put_token_back();
1813         AO = parse_expression(VOID_CONTEXT);
1814         code_generate(AO, VOID_CONTEXT, -1);
1815         if (vivc_flag) { panic_mode_error_recovery(); return; }
1816         goto StatementTerminator;
1817     }
1818
1819     statements.enabled = FALSE;
1820
1821     switch(token_value)
1822     {
1823
1824     /*  -------------------------------------------------------------------- */
1825     /*  box <string-1> ... <string-n> -------------------------------------- */
1826     /*  -------------------------------------------------------------------- */
1827
1828         case BOX_CODE:
1829             INITAOT(&AO3, CONSTANT_OT);
1830                  AO3.value = begin_table_array();
1831                  AO3.marker = ARRAY_MV;
1832                  ln = 0; ln2 = 0;
1833                  do
1834                  {   get_next_token();
1835                      if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP))
1836                          break;
1837                      if (token_type != DQ_TT)
1838                          ebf_curtoken_error("text of box line in double-quotes");
1839                      {   int i, j;
1840                          for (i=0, j=0; token_text[i] != 0; j++)
1841                              if (token_text[i] == '@')
1842                              {   if (token_text[i+1] == '@')
1843                                  {   i = i + 2;
1844                                      while (isdigit(token_text[i])) i++;
1845                                  }
1846                                  else
1847                                  {   i++;
1848                                      if (token_text[i] != 0) i++;
1849                                      if (token_text[i] != 0) i++;
1850                                  }
1851                              }
1852                              else i++;
1853                          if (j > ln2) ln2 = j;
1854                      }
1855                      put_token_back();
1856                      array_entry(ln++, FALSE, parse_expression(CONSTANT_CONTEXT));
1857                  } while (TRUE);
1858                  finish_array(ln, FALSE);
1859                  if (ln == 0)
1860                      error("No lines of text given for 'box' display");
1861
1862                  INITAO(&AO2);
1863                  AO2.value = ln2; set_constant_ot(&AO2);
1864                  assembleg_call_2(veneer_routine(Box__Routine_VR),
1865                      AO2, AO3, zero_operand);
1866                  return;
1867
1868     /*  -------------------------------------------------------------------- */
1869     /*  break -------------------------------------------------------------- */
1870     /*  -------------------------------------------------------------------- */
1871
1872         case BREAK_CODE:
1873                  if (break_label == -1)
1874                  error("'break' can only be used in a loop or 'switch' block");
1875                  else
1876                      assembleg_jump(break_label);
1877                  break;
1878
1879     /*  -------------------------------------------------------------------- */
1880     /*  continue ----------------------------------------------------------- */
1881     /*  -------------------------------------------------------------------- */
1882
1883         case CONTINUE_CODE:
1884                  if (continue_label == -1)
1885                  error("'continue' can only be used in a loop block");
1886                  else
1887                      assembleg_jump(continue_label);
1888                  break;
1889
1890     /*  -------------------------------------------------------------------- */
1891     /*  do <codeblock> until (<condition>) --------------------------------- */
1892     /*  -------------------------------------------------------------------- */
1893
1894         case DO_CODE:
1895                  assemble_label_no(ln = next_label++);
1896                  ln2 = next_label++; ln3 = next_label++;
1897                  parse_code_block(ln3, ln2, 0);
1898                  statements.enabled = TRUE;
1899                  get_next_token();
1900                  if ((token_type == STATEMENT_TT)
1901                      && (token_value == UNTIL_CODE))
1902                  {   assemble_forward_label_no(ln2);
1903                      match_open_bracket();
1904                      AO = parse_expression(CONDITION_CONTEXT);
1905                      match_close_bracket();
1906                      code_generate(AO, CONDITION_CONTEXT, ln);
1907                  }
1908                  else error("'do' without matching 'until'");
1909
1910                  assemble_forward_label_no(ln3);
1911                  break;
1912
1913     /*  -------------------------------------------------------------------- */
1914     /*  font on/off -------------------------------------------------------- */
1915     /*  -------------------------------------------------------------------- */
1916
1917         case FONT_CODE:
1918                  misc_keywords.enabled = TRUE;
1919                  get_next_token();
1920                  misc_keywords.enabled = FALSE;
1921                  if ((token_type != MISC_KEYWORD_TT)
1922                      || ((token_value != ON_MK)
1923                          && (token_value != OFF_MK)))
1924                  {   ebf_curtoken_error("'on' or 'off'");
1925                      panic_mode_error_recovery();
1926                      break;
1927                  }
1928
1929                  /* Call glk_set_style(normal or preformatted) */
1930                  INITAO(&AO);
1931                  AO.value = 0x0086;
1932                  set_constant_ot(&AO);
1933                  if (token_value == ON_MK)
1934                    AO2 = zero_operand;
1935                  else 
1936                    AO2 = two_operand;
1937                  assembleg_call_2(veneer_routine(Glk__Wrap_VR), 
1938                    AO, AO2, zero_operand);
1939                  break;
1940
1941     /*  -------------------------------------------------------------------- */
1942     /*  for (<initialisation> : <continue-condition> : <updating>) --------- */
1943     /*  -------------------------------------------------------------------- */
1944
1945         /*  Note that it's legal for any or all of the three sections of a
1946             'for' specification to be empty.  This 'for' implementation
1947             often wastes 3 bytes with a redundant branch rather than keep
1948             expression parse trees for long periods (as previous versions
1949             of Inform did, somewhat crudely by simply storing the textual
1950             form of a 'for' loop).  It is adequate for now.                  */
1951
1952         case FOR_CODE:
1953                  match_open_bracket();
1954                  get_next_token();
1955
1956                  /*  Initialisation code  */
1957                  AO.type = OMITTED_OT;
1958                  spare_debug_location1 = statement_debug_location;
1959                  AO2.type = OMITTED_OT; flag = 0;
1960                  spare_debug_location2 = statement_debug_location;
1961
1962                  if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1963                  {   put_token_back();
1964                      if (!((token_type==SEP_TT)&&(token_value==SUPERCLASS_SEP)))
1965                      {   sequence_point_follows = TRUE;
1966                          statement_debug_location = get_token_location();
1967                          code_generate(parse_expression(FORINIT_CONTEXT),
1968                              VOID_CONTEXT, -1);
1969                      }
1970                      get_next_token();
1971                      if ((token_type==SEP_TT)&&(token_value == SUPERCLASS_SEP))
1972                      {   get_next_token();
1973                          if ((token_type==SEP_TT)&&(token_value == CLOSEB_SEP))
1974                          {   assemble_label_no(ln = next_label++);
1975                              ln2 = next_label++;
1976                              parse_code_block(ln2, ln, 0);
1977                              sequence_point_follows = FALSE;
1978                              if (!execution_never_reaches_here)
1979                                  assembleg_jump(ln);
1980                              assemble_forward_label_no(ln2);
1981                              return;
1982                          }
1983                          goto ParseUpdate;
1984                      }
1985                      put_token_back();
1986                      if (!match_colon()) break;
1987                  }
1988
1989                  get_next_token();
1990                  if (!((token_type==SEP_TT)&&(token_value==COLON_SEP)))
1991                  {   put_token_back();
1992                      spare_debug_location1 = get_token_location();
1993                      AO = parse_expression(CONDITION_CONTEXT);
1994                      if (!match_colon()) break;
1995                  }
1996                  get_next_token();
1997
1998                  ParseUpdate:
1999                  if (!((token_type==SEP_TT)&&(token_value==CLOSEB_SEP)))
2000                  {   put_token_back();
2001                      spare_debug_location2 = get_token_location();
2002                      AO2 = parse_expression(VOID_CONTEXT);
2003                      match_close_bracket();
2004                      flag = test_for_incdec(AO2);
2005                  }
2006
2007                  ln = next_label++;
2008                  ln2 = next_label++;
2009                  ln3 = next_label++;
2010
2011                  if ((AO2.type == OMITTED_OT) || (flag != 0))
2012                  {
2013                      assemble_label_no(ln);
2014                      if (flag==0) assemble_label_no(ln2);
2015
2016                      /*  The "finished yet?" condition  */
2017
2018                      if (AO.type != OMITTED_OT)
2019                      {   sequence_point_follows = TRUE;
2020                          statement_debug_location = spare_debug_location1;
2021                          code_generate(AO, CONDITION_CONTEXT, ln3);
2022                      }
2023
2024                  }
2025                  else
2026                  {
2027                      /*  This is the jump which could be avoided with the aid
2028                          of long-term expression storage  */
2029
2030                      sequence_point_follows = FALSE;
2031                      assembleg_jump(ln2);
2032
2033                      /*  The "update" part  */
2034
2035                      assemble_label_no(ln);
2036                      sequence_point_follows = TRUE;
2037                      statement_debug_location = spare_debug_location2;
2038                      code_generate(AO2, VOID_CONTEXT, -1);
2039
2040                      assemble_label_no(ln2);
2041
2042                      /*  The "finished yet?" condition  */
2043
2044                      if (AO.type != OMITTED_OT)
2045                      {   sequence_point_follows = TRUE;
2046                          statement_debug_location = spare_debug_location1;
2047                          code_generate(AO, CONDITION_CONTEXT, ln3);
2048                      }
2049                  }
2050
2051                  if (flag != 0)
2052                  {
2053                      /*  In this optimised case, update code is at the end
2054                          of the loop block, so "continue" goes there  */
2055
2056                      parse_code_block(ln3, ln2, 0);
2057                      assemble_label_no(ln2);
2058
2059                      sequence_point_follows = TRUE;
2060                      statement_debug_location = spare_debug_location2;
2061                      if (flag > 0)
2062                      {   INITAO(&AO3);
2063                          AO3.value = flag;
2064                          if (AO3.value >= MAX_LOCAL_VARIABLES)
2065                            AO3.type = GLOBALVAR_OT;
2066                          else
2067                            AO3.type = LOCALVAR_OT;
2068                          assembleg_3(add_gc, AO3, one_operand, AO3);
2069                      }
2070                      else
2071                      {   INITAO(&AO3);
2072                          AO3.value = -flag;
2073                          if (AO3.value >= MAX_LOCAL_VARIABLES)
2074                            AO3.type = GLOBALVAR_OT;
2075                          else
2076                            AO3.type = LOCALVAR_OT;
2077                          assembleg_3(sub_gc, AO3, one_operand, AO3);
2078                      }
2079                      assembleg_jump(ln);
2080                  }
2081                  else
2082                  {
2083                      /*  In the unoptimised case, update code is at the
2084                          start of the loop block, so "continue" goes there  */
2085
2086                      parse_code_block(ln3, ln, 0);
2087                      if (!execution_never_reaches_here)
2088                      {   sequence_point_follows = FALSE;
2089                          assembleg_jump(ln);
2090                      }
2091                  }
2092
2093                  assemble_forward_label_no(ln3);
2094                  return;
2095
2096     /*  -------------------------------------------------------------------- */
2097     /*  give <expression> [~]attr [, [~]attr [, ...]] ---------------------- */
2098     /*  -------------------------------------------------------------------- */
2099
2100         case GIVE_CODE:
2101                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2102                           QUANTITY_CONTEXT, -1);
2103                  check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
2104                  if ((AO.type == LOCALVAR_OT) && (AO.value == 0))
2105                      onstack = TRUE;
2106                  else
2107                      onstack = FALSE;
2108
2109                  do
2110                  {   get_next_token();
2111                      if ((token_type == SEP_TT) 
2112                        && (token_value == SEMICOLON_SEP)) {
2113                          if (onstack) {
2114                            assembleg_2(copy_gc, stack_pointer, zero_operand);
2115                          }
2116                          return;
2117                      }
2118                      if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
2119                          ln = 0;
2120                      else
2121                      {   ln = 1;
2122                          put_token_back();
2123                      }
2124                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2125                                QUANTITY_CONTEXT, -1);
2126                      check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
2127                      if (runtime_error_checking_switch && (!veneer_mode))
2128                      {   ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR);
2129                          if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
2130                            /* already on stack */
2131                          }
2132                          else {
2133                            assembleg_store(stack_pointer, AO2);
2134                          }
2135                          if (onstack)
2136                            assembleg_2(stkpeek_gc, one_operand, stack_pointer);
2137                          else
2138                            assembleg_store(stack_pointer, AO);
2139                          assembleg_3(call_gc, veneer_routine(ln2), two_operand,
2140                            zero_operand);
2141                      }
2142                      else {
2143                          if (is_constant_ot(AO2.type) && AO2.marker == 0) {
2144                            AO2.value += 8;
2145                            set_constant_ot(&AO2);
2146                          }
2147                          else {
2148                            INITAOTV(&AO3, BYTECONSTANT_OT, 8);
2149                            assembleg_3(add_gc, AO2, AO3, stack_pointer);
2150                            AO2 = stack_pointer;
2151                          }
2152                          if (onstack) {
2153                            if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0))
2154                              assembleg_2(stkpeek_gc, one_operand, 
2155                                stack_pointer);
2156                            else
2157                              assembleg_2(stkpeek_gc, zero_operand, 
2158                                stack_pointer);
2159                          }
2160                          if (ln) 
2161                            AO3 = one_operand;
2162                          else
2163                            AO3 = zero_operand;
2164                          assembleg_3(astorebit_gc, AO, AO2, AO3);
2165                      }
2166                  } while(TRUE);
2167
2168     /*  -------------------------------------------------------------------- */
2169     /*  if (<condition>) <codeblock> [else <codeblock>] -------------------- */
2170     /*  -------------------------------------------------------------------- */
2171
2172         case IF_CODE:
2173                  flag = FALSE; /* set if there's an "else" */
2174                  ln2 = 0;
2175                  pre_unreach = execution_never_reaches_here;
2176
2177                  match_open_bracket();
2178                  AO = parse_expression(CONDITION_CONTEXT);
2179                  match_close_bracket();
2180
2181                  statements.enabled = TRUE;
2182                  get_next_token();
2183                  if ((token_type == STATEMENT_TT)&&(token_value == RTRUE_CODE))
2184                      ln = -4;
2185                  else
2186                  if ((token_type == STATEMENT_TT)&&(token_value == RFALSE_CODE))
2187                      ln = -3;
2188                  else
2189                  {   put_token_back();
2190                      ln = next_label++;
2191                  }
2192
2193                  /* The condition */
2194                  code_generate(AO, CONDITION_CONTEXT, ln);
2195
2196                  if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
2197                      /* If the condition never falls through to here, then
2198                         it was an "if (0)" test. Our convention is to skip
2199                         the "not reached" warnings for this case. */
2200                      execution_never_reaches_here |= EXECSTATE_NOWARN;
2201                  }
2202
2203                  /* The "if" block */
2204                  if (ln >= 0) parse_code_block(break_label, continue_label, 0);
2205                  else
2206                  {   get_next_token();
2207                      if ((token_type != SEP_TT)
2208                          || (token_value != SEMICOLON_SEP))
2209                      {   ebf_curtoken_error("';'");
2210                          put_token_back();
2211                      }
2212                  }
2213
2214                  statements.enabled = TRUE;
2215                  get_next_token();
2216                  
2217                  /* An #if directive around the ELSE clause is legal. */
2218                  while ((token_type == SEP_TT) && (token_value == HASH_SEP))
2219                  {   parse_directive(TRUE);
2220                      statements.enabled = TRUE;
2221                      get_next_token();
2222                  }
2223                  
2224                  if ((token_type == STATEMENT_TT) && (token_value == ELSE_CODE))
2225                  {   flag = TRUE;
2226                      if (ln >= 0)
2227                      {   ln2 = next_label++;
2228                          if (!execution_never_reaches_here)
2229                          {   sequence_point_follows = FALSE;
2230                              assembleg_jump(ln2);
2231                          }
2232                      }
2233                  }
2234                  else put_token_back();
2235
2236                  /* The "else" label (or end of statement, if there is no "else") */
2237                  labelexists = FALSE;
2238                  if (ln >= 0) labelexists = assemble_forward_label_no(ln);
2239
2240                  if (flag)
2241                  {
2242                      /* If labelexists is false, then we started with
2243                         "if (1)". In this case, we don't want a "not
2244                         reached" warning on the "else" block. We
2245                         temporarily disable the NOWARN flag, and restore it
2246                         afterwards. */
2247                      int saved_unreach = 0;
2248                      if (execution_never_reaches_here && !labelexists) {
2249                          saved_unreach = execution_never_reaches_here;
2250                          execution_never_reaches_here |= EXECSTATE_NOWARN;
2251                      }
2252
2253                      /* The "else" block */
2254                      parse_code_block(break_label, continue_label, 0);
2255
2256                      if (execution_never_reaches_here && !labelexists) {
2257                          if (saved_unreach & EXECSTATE_NOWARN)
2258                              execution_never_reaches_here |= EXECSTATE_NOWARN;
2259                          else
2260                              execution_never_reaches_here &= ~EXECSTATE_NOWARN;
2261                      }
2262
2263                      /* The post-"else" label */
2264                      if (ln >= 0) assemble_forward_label_no(ln2);
2265                  }
2266                  else
2267                  {
2268                      /* There was no "else". If we're unreachable, then the
2269                         statement returned unconditionally, which means 
2270                         "if (1) return". Skip warnings. */
2271                      if (!pre_unreach && execution_never_reaches_here) {
2272                          execution_never_reaches_here |= EXECSTATE_NOWARN;
2273                      }
2274                  }
2275
2276                  return;
2277
2278     /*  -------------------------------------------------------------------- */
2279     /*  inversion ---------------------------------------------------------- */
2280     /*  -------------------------------------------------------------------- */
2281
2282         case INVERSION_CODE:
2283                  INITAOTV(&AO2, DEREFERENCE_OT, GLULX_HEADER_SIZE+8);
2284                  assembleg_2(copyb_gc, AO2, stack_pointer);
2285                  assembleg_1(streamchar_gc, stack_pointer);
2286                  AO2.value  = GLULX_HEADER_SIZE+9; 
2287                  assembleg_2(copyb_gc, AO2, stack_pointer);
2288                  assembleg_1(streamchar_gc, stack_pointer);
2289                  AO2.value  = GLULX_HEADER_SIZE+10; 
2290                  assembleg_2(copyb_gc, AO2, stack_pointer);
2291                  assembleg_1(streamchar_gc, stack_pointer);
2292                  AO2.value  = GLULX_HEADER_SIZE+11; 
2293                  assembleg_2(copyb_gc, AO2, stack_pointer);
2294                  assembleg_1(streamchar_gc, stack_pointer);
2295
2296                  if (/* DISABLES CODE */ (0)) {
2297                      INITAO(&AO);
2298                      AO.value = '(';
2299                      set_constant_ot(&AO);
2300                      assembleg_1(streamchar_gc, AO);
2301                      AO.value = 'G';
2302                      set_constant_ot(&AO);
2303                      assembleg_1(streamchar_gc, AO);
2304
2305                      AO2.value  = GLULX_HEADER_SIZE+12; 
2306                      assembleg_2(copyb_gc, AO2, stack_pointer);
2307                      assembleg_1(streamchar_gc, stack_pointer);
2308                      AO2.value  = GLULX_HEADER_SIZE+13; 
2309                      assembleg_2(copyb_gc, AO2, stack_pointer);
2310                      assembleg_1(streamchar_gc, stack_pointer);
2311                      AO2.value  = GLULX_HEADER_SIZE+14; 
2312                      assembleg_2(copyb_gc, AO2, stack_pointer);
2313                      assembleg_1(streamchar_gc, stack_pointer);
2314                      AO2.value  = GLULX_HEADER_SIZE+15; 
2315                      assembleg_2(copyb_gc, AO2, stack_pointer);
2316                      assembleg_1(streamchar_gc, stack_pointer);
2317
2318                      AO.marker = 0;
2319                      AO.value = ')';
2320                      set_constant_ot(&AO);
2321                      assembleg_1(streamchar_gc, AO);
2322                  }
2323
2324                  break;
2325
2326     /*  -------------------------------------------------------------------- */
2327     /*  jump <label> ------------------------------------------------------- */
2328     /*  -------------------------------------------------------------------- */
2329
2330         case JUMP_CODE:
2331                  assembleg_jump(parse_label());
2332                  break;
2333
2334     /*  -------------------------------------------------------------------- */
2335     /*  move <expression> to <expression> ---------------------------------- */
2336     /*  -------------------------------------------------------------------- */
2337
2338         case MOVE_CODE:
2339                  misc_keywords.enabled = TRUE;
2340                  AO = parse_expression(QUANTITY_CONTEXT);
2341
2342                  get_next_token();
2343                  misc_keywords.enabled = FALSE;
2344                  if ((token_type != MISC_KEYWORD_TT)
2345                      || (token_value != TO_MK))
2346                  {   ebf_curtoken_error("'to'");
2347                      panic_mode_error_recovery();
2348                      return;
2349                  }
2350
2351                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2352                      QUANTITY_CONTEXT, -1);
2353                  AO = code_generate(AO, QUANTITY_CONTEXT, -1);
2354                  check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
2355                  check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
2356                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
2357                      assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2,
2358                          zero_operand);
2359                  else
2360                      assembleg_call_2(veneer_routine(OB__Move_VR), AO, AO2,
2361                          zero_operand);
2362                  break;
2363
2364     /*  -------------------------------------------------------------------- */
2365     /*  new_line ----------------------------------------------------------- */
2366     /*  -------------------------------------------------------------------- */
2367
2368         case NEW_LINE_CODE:  
2369               INITAOTV(&AO, BYTECONSTANT_OT, 0x0A);
2370               assembleg_1(streamchar_gc, AO); 
2371               break;
2372
2373     /*  -------------------------------------------------------------------- */
2374     /*  objectloop (<initialisation>) <codeblock> -------------------------- */
2375     /*  -------------------------------------------------------------------- */
2376
2377         case OBJECTLOOP_CODE:
2378
2379                  match_open_bracket();
2380                  get_next_token();
2381                  if (token_type == LOCAL_VARIABLE_TT) {
2382                      INITAOTV(&AO, LOCALVAR_OT, token_value);
2383                  }
2384                  else if ((token_type == SYMBOL_TT) &&
2385                    (symbols[token_value].type == GLOBAL_VARIABLE_T)) {
2386                      INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
2387                  }
2388                  else {
2389                      ebf_curtoken_error("'objectloop' variable");
2390                      panic_mode_error_recovery(); 
2391                      break;
2392                  }
2393                  misc_keywords.enabled = TRUE;
2394                  get_next_token(); flag = TRUE;
2395                  misc_keywords.enabled = FALSE;
2396                  if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
2397                      flag = FALSE;
2398
2399                  ln = 0;
2400                  if ((token_type == MISC_KEYWORD_TT)
2401                      && (token_value == NEAR_MK)) ln = 1;
2402                  if ((token_type == MISC_KEYWORD_TT)
2403                      && (token_value == FROM_MK)) ln = 2;
2404                  if ((token_type == CND_TT) && (token_value == IN_COND))
2405                  {   get_next_token();
2406                      get_next_token();
2407                      if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP))
2408                          ln = 3;
2409                      put_token_back();
2410                      put_token_back();
2411                  }
2412
2413                  if (ln != 0) {
2414                    /*  Old style (Inform 5) objectloops: note that we
2415                        implement objectloop (a in b) in the old way since
2416                        this runs through objects in a different order from
2417                        the new way, and there may be existing Inform code
2418                        relying on this.                                    */
2419                      assembly_operand AO4, AO5;
2420                      INITAO(&AO5);
2421
2422                      sequence_point_follows = TRUE;
2423                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2424                          QUANTITY_CONTEXT, -1);
2425                      match_close_bracket();
2426                      if (ln == 1) {
2427                          if (runtime_error_checking_switch)
2428                              AO2 = check_nonzero_at_runtime(AO2, -1,
2429                                  OBJECTLOOP_RTE);
2430                          INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
2431                          assembleg_3(aload_gc, AO2, AO4, stack_pointer);
2432                          INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
2433                          assembleg_3(aload_gc, stack_pointer, AO4, stack_pointer);
2434                          AO2 = stack_pointer;
2435                      }
2436                      else if (ln == 3) {
2437                          if (runtime_error_checking_switch) {
2438                              AO5 = AO2;
2439                              AO2 = check_nonzero_at_runtime(AO2, -1,
2440                                  CHILD_RTE);
2441                          }
2442                          INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHILD());
2443                          assembleg_3(aload_gc, AO2, AO4, stack_pointer);
2444                          AO2 = stack_pointer;
2445                      }
2446                      else {
2447                          /* do nothing */
2448                      }
2449                      assembleg_store(AO, AO2);
2450                      assembleg_1_branch(jz_gc, AO, ln2 = next_label++);
2451                      assemble_label_no(ln4 = next_label++);
2452                      parse_code_block(ln2, ln3 = next_label++, 0);
2453                      sequence_point_follows = FALSE;
2454                      assemble_label_no(ln3);
2455                      if (runtime_error_checking_switch) {
2456                          AO2 = check_nonzero_at_runtime(AO, ln2,
2457                               OBJECTLOOP2_RTE);
2458                          if ((ln == 3)
2459                              && ((AO5.type != LOCALVAR_OT)||(AO5.value != 0))
2460                              && ((AO5.type != LOCALVAR_OT)||(AO5.value != AO.value)))
2461                          {   assembly_operand en_ao;
2462                              INITAO(&en_ao);
2463                              en_ao.value = OBJECTLOOP_BROKEN_RTE;
2464                              set_constant_ot(&en_ao);
2465                              INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_PARENT());
2466                              assembleg_3(aload_gc, AO, AO4, stack_pointer);
2467                              assembleg_2_branch(jeq_gc, stack_pointer, AO5, 
2468                                  next_label);
2469                              assembleg_call_2(veneer_routine(RT__Err_VR),
2470                                  en_ao, AO, zero_operand);
2471                              assembleg_jump(ln2);
2472                              assemble_label_no(next_label++);
2473                          }
2474                      }
2475                      else {
2476                          AO2 = AO;
2477                      }
2478                      INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_SIBLING());
2479                      assembleg_3(aload_gc, AO2, AO4, AO);
2480                      assembleg_1_branch(jnz_gc, AO, ln4);
2481                      assemble_label_no(ln2);
2482                      return;
2483                  }
2484
2485                  sequence_point_follows = TRUE;
2486                  ln = get_symbol_index("Class");
2487                  if (ln < 0) {
2488                      error("No 'Class' object found");
2489                      AO2 = zero_operand;
2490                  }
2491                  else {
2492                      INITAOT(&AO2, CONSTANT_OT);
2493                      AO2.value = symbols[ln].value;
2494                      AO2.marker = OBJECT_MV;
2495                  }
2496                  assembleg_store(AO, AO2);
2497
2498                  assemble_label_no(ln = next_label++);
2499                  ln2 = next_label++;
2500                  ln3 = next_label++;
2501                  if (flag)
2502                  {   put_token_back();
2503                      put_token_back();
2504                      sequence_point_follows = TRUE;
2505                      code_generate(parse_expression(CONDITION_CONTEXT),
2506                          CONDITION_CONTEXT, ln3);
2507                      match_close_bracket();
2508                  }
2509                  parse_code_block(ln2, ln3, 0);
2510
2511                  sequence_point_follows = FALSE;
2512                  assemble_label_no(ln3);
2513                  INITAOTV(&AO4, BYTECONSTANT_OT, GOBJFIELD_CHAIN());
2514                  assembleg_3(aload_gc, AO, AO4, AO);
2515                  assembleg_1_branch(jnz_gc, AO, ln);
2516                  assemble_label_no(ln2);
2517                  return;
2518
2519     /*  -------------------------------------------------------------------- */
2520     /*  (see routine above) ------------------------------------------------ */
2521     /*  -------------------------------------------------------------------- */
2522
2523         case PRINT_CODE:
2524             get_next_token();
2525             parse_print_g(FALSE); return;
2526         case PRINT_RET_CODE:
2527             get_next_token();
2528             parse_print_g(TRUE); return;
2529
2530     /*  -------------------------------------------------------------------- */
2531     /*  quit --------------------------------------------------------------- */
2532     /*  -------------------------------------------------------------------- */
2533
2534         case QUIT_CODE:
2535                  assembleg_0(quit_gc); break;
2536
2537     /*  -------------------------------------------------------------------- */
2538     /*  remove <expression> ------------------------------------------------ */
2539     /*  -------------------------------------------------------------------- */
2540
2541         case REMOVE_CODE:
2542                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2543                      QUANTITY_CONTEXT, -1);
2544                  check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
2545                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
2546                      assembleg_call_1(veneer_routine(RT__ChR_VR), AO,
2547                          zero_operand);
2548                  else
2549                      assembleg_call_1(veneer_routine(OB__Remove_VR), AO,
2550                          zero_operand);
2551                  break;
2552
2553     /*  -------------------------------------------------------------------- */
2554     /*  return [<expression>] ---------------------------------------------- */
2555     /*  -------------------------------------------------------------------- */
2556
2557         case RETURN_CODE:
2558           get_next_token();
2559           if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) {
2560             assembleg_1(return_gc, one_operand); 
2561             return; 
2562           }
2563           put_token_back();
2564           AO = code_generate(parse_expression(RETURN_Q_CONTEXT),
2565             QUANTITY_CONTEXT, -1);
2566           assembleg_1(return_gc, AO);
2567           break;
2568
2569     /*  -------------------------------------------------------------------- */
2570     /*  rfalse ------------------------------------------------------------- */
2571     /*  -------------------------------------------------------------------- */
2572
2573         case RFALSE_CODE:   
2574           assembleg_1(return_gc, zero_operand); 
2575           break;
2576
2577     /*  -------------------------------------------------------------------- */
2578     /*  rtrue -------------------------------------------------------------- */
2579     /*  -------------------------------------------------------------------- */
2580
2581         case RTRUE_CODE:   
2582           assembleg_1(return_gc, one_operand); 
2583           break;
2584
2585     /*  -------------------------------------------------------------------- */
2586     /*  spaces <expression> ------------------------------------------------ */
2587     /*  -------------------------------------------------------------------- */
2588
2589         case SPACES_CODE:
2590                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2591                      QUANTITY_CONTEXT, -1);
2592
2593                  assembleg_store(temp_var1, AO);
2594
2595                  INITAO(&AO);
2596                  AO.value = 32; set_constant_ot(&AO);
2597
2598                  assembleg_2_branch(jlt_gc, temp_var1, one_operand, 
2599                      ln = next_label++);
2600                  assemble_label_no(ln2 = next_label++);
2601                  assembleg_1(streamchar_gc, AO);
2602                  assembleg_dec(temp_var1);
2603                  assembleg_1_branch(jnz_gc, temp_var1, ln2);
2604                  assemble_label_no(ln);
2605                  break;
2606
2607     /*  -------------------------------------------------------------------- */
2608     /*  string <expression> <literal-string> ------------------------------- */
2609     /*  -------------------------------------------------------------------- */
2610
2611         case STRING_CODE:
2612                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
2613                      QUANTITY_CONTEXT, -1);
2614                  if (is_constant_ot(AO2.type) && AO2.marker == 0) {
2615                      /* Compile-time check */
2616                      if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
2617                          error_max_dynamic_strings(AO2.value);
2618                      }
2619                  }
2620                  get_next_token();
2621                  if (token_type == DQ_TT)
2622                  {   INITAOT(&AO4, CONSTANT_OT);
2623                      /* This is not actually placed in low memory; Glulx
2624                         has no such concept. We use the LOWSTRING flag
2625                         for compatibility with older compiler behavior. */
2626                      AO4.value = compile_string(token_text, STRCTX_LOWSTRING);
2627                      AO4.marker = STRING_MV;
2628                  }
2629                  else
2630                  {   put_token_back();
2631                      AO4 = parse_expression(CONSTANT_CONTEXT);
2632                  }
2633                  assembleg_call_2(veneer_routine(Dynam__String_VR),
2634                    AO2, AO4, zero_operand);
2635                  break;
2636
2637     /*  -------------------------------------------------------------------- */
2638     /*  style roman/reverse/bold/underline/fixed --------------------------- */
2639     /*  -------------------------------------------------------------------- */
2640
2641         case STYLE_CODE:
2642                  misc_keywords.enabled = TRUE;
2643                  get_next_token();
2644                  misc_keywords.enabled = FALSE;
2645                  if ((token_type != MISC_KEYWORD_TT)
2646                      || ((token_value != ROMAN_MK)
2647                          && (token_value != REVERSE_MK)
2648                          && (token_value != BOLD_MK)
2649                          && (token_value != UNDERLINE_MK)
2650                          && (token_value != FIXED_MK)))
2651                  {   ebf_curtoken_error(
2652 "'roman', 'bold', 'underline', 'reverse' or 'fixed'");
2653                      panic_mode_error_recovery();
2654                      break;
2655                  }
2656
2657                  /* Call glk_set_style() */
2658
2659                  INITAO(&AO);
2660                  AO.value = 0x0086;
2661                  set_constant_ot(&AO);
2662                  switch(token_value)
2663                  {   case ROMAN_MK:
2664                      default: 
2665                          AO2 = zero_operand; /* normal */
2666                          break;
2667                      case REVERSE_MK: 
2668                          INITAO(&AO2);
2669                          AO2.value = 5; /* alert */
2670                          set_constant_ot(&AO2);
2671                          break;
2672                      case BOLD_MK: 
2673                          INITAO(&AO2);
2674                          AO2.value = 4; /* subheader */
2675                          set_constant_ot(&AO2);
2676                          break;
2677                      case UNDERLINE_MK: 
2678                          AO2 = one_operand; /* emphasized */
2679                          break;
2680                      case FIXED_MK: 
2681                          AO2 = two_operand; /* preformatted */
2682                          break;
2683                  }
2684                  assembleg_call_2(veneer_routine(Glk__Wrap_VR), 
2685                    AO, AO2, zero_operand);
2686                  break;
2687
2688     /*  -------------------------------------------------------------------- */
2689     /*  switch (<expression>) <codeblock> ---------------------------------- */
2690     /*  -------------------------------------------------------------------- */
2691
2692         case SWITCH_CODE:
2693                  match_open_bracket();
2694                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
2695                      QUANTITY_CONTEXT, -1);
2696                  match_close_bracket();
2697
2698                  assembleg_store(temp_var1, AO); 
2699
2700                  parse_code_block(ln = next_label++, continue_label, 1);
2701                  assemble_forward_label_no(ln);
2702                  return;
2703
2704     /*  -------------------------------------------------------------------- */
2705     /*  while (<condition>) <codeblock> ------------------------------------ */
2706     /*  -------------------------------------------------------------------- */
2707
2708         case WHILE_CODE:
2709                  assemble_label_no(ln = next_label++);
2710                  match_open_bracket();
2711
2712                  code_generate(parse_expression(CONDITION_CONTEXT),
2713                      CONDITION_CONTEXT, ln2 = next_label++);
2714                  match_close_bracket();
2715
2716                  parse_code_block(ln2, ln, 0);
2717                  sequence_point_follows = FALSE;
2718                  assembleg_jump(ln);
2719                  assemble_forward_label_no(ln2);
2720                  return;
2721
2722     /*  -------------------------------------------------------------------- */
2723
2724         case SDEFAULT_CODE:
2725                  error("'default' without matching 'switch'"); break;
2726         case ELSE_CODE:
2727                  error("'else' without matching 'if'"); break;
2728         case UNTIL_CODE:
2729                  error("'until' without matching 'do'");
2730                  panic_mode_error_recovery(); return;
2731
2732     /*  -------------------------------------------------------------------- */
2733
2734     /* And a useful default, which will never be triggered in a complete
2735        Inform compiler, but which is important in development. */
2736
2737         default:
2738           error("*** Statement code gen: Can't generate yet ***\n");
2739           panic_mode_error_recovery(); return;
2740     }
2741
2742     StatementTerminator:
2743
2744     get_next_token();
2745     if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
2746     {   ebf_curtoken_error("';'");
2747         put_token_back();
2748     }
2749 }
2750
2751 extern void parse_statement(int break_label, int continue_label)
2752 {
2753     int res;
2754     int saved_entire_flag;
2755     
2756     res = parse_named_label_statements();
2757     if (!res)
2758         return;
2759
2760     saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
2761     if (execution_never_reaches_here)
2762         execution_never_reaches_here |= EXECSTATE_ENTIRE;
2763  
2764     if (!glulx_mode)
2765         parse_statement_z(break_label, continue_label);
2766     else
2767         parse_statement_g(break_label, continue_label);
2768
2769     if (saved_entire_flag)
2770         execution_never_reaches_here |= EXECSTATE_ENTIRE;
2771     else
2772         execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
2773 }
2774
2775 /* This does the same work as parse_statement(), but it's called if you've
2776    already parsed an expression (in void context) and you want to generate
2777    it as a statement. Essentially it's a copy of parse_statement() and
2778    parse_statement_z/g(), except we skip straight to the "expression-with-
2779    side-effects" bit and omit everything else.
2780
2781    The caller doesn't need to pass break_label/continue_label; they're
2782    not used for this code path.
2783 */
2784 extern void parse_statement_singleexpr(assembly_operand AO)
2785 {
2786     int res;
2787     int saved_entire_flag;
2788     
2789     res = parse_named_label_statements();
2790     if (!res)
2791         return;
2792
2793     saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
2794     if (execution_never_reaches_here)
2795         execution_never_reaches_here |= EXECSTATE_ENTIRE;
2796
2797     code_generate(AO, VOID_CONTEXT, -1);
2798     
2799     if (vivc_flag) {
2800         panic_mode_error_recovery();
2801     }
2802     else {
2803         /* StatementTerminator... */
2804         get_next_token();
2805         if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
2806         {   ebf_curtoken_error("';'");
2807             put_token_back();
2808         }
2809     }
2810
2811     if (saved_entire_flag)
2812         execution_never_reaches_here |= EXECSTATE_ENTIRE;
2813     else
2814         execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
2815 }
2816
2817 /* ========================================================================= */
2818 /*   Data structure management routines                                      */
2819 /* ------------------------------------------------------------------------- */
2820
2821 extern void init_states_vars(void)
2822 {
2823 }
2824
2825 extern void states_begin_pass(void)
2826 {
2827 }
2828
2829 extern void states_allocate_arrays(void)
2830 {
2831 }
2832
2833 extern void states_free_arrays(void)
2834 {
2835 }
2836
2837 /* ========================================================================= */