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