1 /* ------------------------------------------------------------------------- */
2 /* "syntax" : Syntax analyser and compiler */
4 /* Copyright (c) Graham Nelson 1993 - 2020 */
6 /* This file is part of Inform. */
8 /* Inform is free software: you can redistribute it and/or modify */
9 /* it under the terms of the GNU General Public License as published by */
10 /* the Free Software Foundation, either version 3 of the License, or */
11 /* (at your option) any later version. */
13 /* Inform is distributed in the hope that it will be useful, */
14 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
15 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
16 /* GNU General Public License for more details. */
18 /* You should have received a copy of the GNU General Public License */
19 /* along with Inform. If not, see https://gnu.org/licenses/ */
21 /* ------------------------------------------------------------------------- */
25 static char *lexical_source;
27 int no_syntax_lines; /* Syntax line count */
29 static void begin_syntax_line(int statement_mode)
31 next_token_begins_syntax_line = TRUE;
33 clear_expression_space();
35 { statements.enabled = TRUE;
36 conditions.enabled = TRUE;
37 local_variables.enabled = TRUE;
38 system_functions.enabled = TRUE;
40 misc_keywords.enabled = FALSE;
41 directive_keywords.enabled = FALSE;
42 directives.enabled = FALSE;
43 segment_markers.enabled = FALSE;
44 opcode_names.enabled = FALSE;
47 { directives.enabled = TRUE;
48 segment_markers.enabled = TRUE;
50 statements.enabled = FALSE;
51 misc_keywords.enabled = FALSE;
52 directive_keywords.enabled = FALSE;
53 local_variables.enabled = FALSE;
54 system_functions.enabled = FALSE;
55 conditions.enabled = FALSE;
56 opcode_names.enabled = FALSE;
59 sequence_point_follows = TRUE;
63 statement_debug_location = get_token_location();
68 extern void panic_mode_error_recovery(void)
70 /* Consume tokens until the next semicolon (or end of file).
71 This is typically called after a syntax error, in hopes of
72 getting parsing back on track. */
74 while ((token_type != EOF_TT)
75 && ((token_type != SEP_TT)||(token_value != SEMICOLON_SEP)))
80 extern void get_next_token_with_directives(void)
82 /* A higher-level version of get_next_token(), which detects and
83 obeys directives such as #ifdef/#ifnot/#endif. (The # sign is
84 required in this case.)
86 This is called while parsing a long construct, such as Class or
87 Object, where we want to support internal #ifdefs. (Although
88 function-parsing predates this and doesn't make use of it.)
90 (Technically this permits *any* #-directive, which means you
91 can define global variables or properties or what-have-you in
92 the middle of an object. You can do that in the middle of an
93 object, too. Don't. It's about as well-supported as Wile E.
94 Coyote one beat before the plummet-lines kick in.) */
96 int directives_save, segment_markers_save, statements_save;
102 /* If the first token is not a '#', return it directly. */
103 if ((token_type != SEP_TT) || (token_value != HASH_SEP))
106 /* Save the lexer flags, and set up for directive parsing. */
107 directives_save = directives.enabled;
108 segment_markers_save = segment_markers.enabled;
109 statements_save = statements.enabled;
111 directives.enabled = TRUE;
112 segment_markers.enabled = FALSE;
113 statements.enabled = FALSE;
114 conditions.enabled = FALSE;
115 local_variables.enabled = FALSE;
116 misc_keywords.enabled = FALSE;
117 system_functions.enabled = FALSE;
121 if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
122 { error("It is illegal to nest a routine inside an object using '#['");
126 if (token_type == DIRECTIVE_TT)
127 parse_given_directive(TRUE);
129 { ebf_error("directive", token_text);
133 /* Restore all the lexer flags. (We are squashing several of them
134 into a single save variable, which I think is safe because that's
137 directive_keywords.enabled = FALSE;
138 directives.enabled = directives_save;
139 segment_markers.enabled = segment_markers_save;
142 local_variables.enabled =
143 misc_keywords.enabled =
144 system_functions.enabled = statements_save;
148 extern void parse_program(char *source)
150 lexical_source = source;
151 while (parse_directive(FALSE)) ;
154 extern int parse_directive(int internal_flag)
156 /* Internal_flag is FALSE if the directive is encountered normally,
157 TRUE if encountered with a # prefix inside a routine or object
160 Returns: TRUE if program continues, FALSE if end of file reached. */
162 int routine_symbol, rep_symbol;
165 begin_syntax_line(FALSE);
168 if (token_type == EOF_TT) return(FALSE);
170 if ((token_type == SEP_TT) && (token_value == HASH_SEP))
173 if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
175 { error("It is illegal to nest routines using '#['");
179 directives.enabled = FALSE;
180 directive_keywords.enabled = FALSE;
181 segment_markers.enabled = FALSE;
183 /* The upcoming symbol is a definition; don't count it as a
184 top-level reference *to* the function. */
185 df_dont_note_global_symbols = TRUE;
187 df_dont_note_global_symbols = FALSE;
188 if ((token_type != SYMBOL_TT)
189 || ((!(sflags[token_value] & UNKNOWN_SFLAG))
190 && (!(sflags[token_value] & REPLACE_SFLAG))))
191 { ebf_error("routine name", token_text);
195 routine_symbol = token_value;
197 rep_symbol = routine_symbol;
198 is_renamed = find_symbol_replacement(&rep_symbol);
200 if ((sflags[routine_symbol] & REPLACE_SFLAG)
201 && !is_renamed && (is_systemfile()))
202 { /* The function is definitely being replaced (system_file
203 always loses priority in a replacement) but is not
204 being renamed to something else. Skip its definition
206 dont_enter_into_symbol_table = TRUE;
209 } while (!((token_type == EOF_TT)
210 || ((token_type==SEP_TT)
211 && (token_value==CLOSE_SQUARE_SEP))));
212 dont_enter_into_symbol_table = FALSE;
213 if (token_type == EOF_TT) return FALSE;
216 { /* Parse the function definition and assign its symbol. */
217 assign_symbol(routine_symbol,
218 parse_routine(lexical_source, FALSE,
219 (char *) symbs[routine_symbol], FALSE, routine_symbol),
221 slines[routine_symbol] = routine_starts_line;
225 /* This function was subject to a "Replace X Y" directive.
226 The first time we see a definition for symbol X, we
227 copy it to Y -- that's the "original" form of the
229 if (svals[rep_symbol] == 0) {
230 assign_symbol(rep_symbol, svals[routine_symbol], ROUTINE_T);
235 if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
236 { ebf_error("';' after ']'", token_text);
242 if ((token_type == SYMBOL_TT) && (stypes[token_value] == CLASS_T))
244 { error("It is illegal to nest an object in a routine using '#classname'");
247 sflags[token_value] |= USED_SFLAG;
248 make_object(FALSE, NULL, -1, -1, svals[token_value]);
252 if (token_type != DIRECTIVE_TT)
253 { /* If we're internal, we expect only a directive here. If
254 we're top-level, the possibilities are broader. */
256 ebf_error("directive", token_text);
258 ebf_error("directive, '[' or class name", token_text);
259 panic_mode_error_recovery();
263 return !(parse_given_directive(internal_flag));
266 static int switch_sign(void)
268 if ((token_type == SEP_TT)&&(token_value == COLON_SEP)) return 1;
269 if ((token_type == SEP_TT)&&(token_value == COMMA_SEP)) return 2;
270 if ((token_type==MISC_KEYWORD_TT)&&(token_value==TO_MK)) return 3;
274 static assembly_operand spec_stack[32];
275 static int spec_type[32];
277 static void compile_alternatives_z(assembly_operand switch_value, int n,
278 int stack_level, int label, int flag)
281 assemblez_2_branch(je_zc, switch_value,
282 spec_stack[stack_level],
283 label, flag); return;
285 assemblez_3_branch(je_zc, switch_value,
286 spec_stack[stack_level], spec_stack[stack_level+1],
287 label, flag); return;
289 assemblez_4_branch(je_zc, switch_value,
290 spec_stack[stack_level], spec_stack[stack_level+1],
291 spec_stack[stack_level+2],
292 label, flag); return;
296 static void compile_alternatives_g(assembly_operand switch_value, int n,
297 int stack_level, int label, int flag)
299 int the_zc = (flag) ? jeq_gc : jne_gc;
302 assembleg_2_branch(the_zc, switch_value,
303 spec_stack[stack_level],
307 error("*** Cannot generate multi-equality tests in Glulx ***");
311 static void compile_alternatives(assembly_operand switch_value, int n,
312 int stack_level, int label, int flag)
315 compile_alternatives_z(switch_value, n, stack_level, label, flag);
317 compile_alternatives_g(switch_value, n, stack_level, label, flag);
320 static void parse_switch_spec(assembly_operand switch_value, int label,
323 int i, j, label_after = -1, spec_sp = 0;
324 int max_equality_args = ((!glulx_mode) ? 3 : 1);
326 sequence_point_follows = FALSE;
330 { error("At most 32 values can be given in a single 'switch' case");
331 panic_mode_error_recovery();
337 if (token_type == SQ_TT || token_type == DQ_TT) {
338 ebf_error("action (or fake action) name", token_text);
341 spec_stack[spec_sp] = action_of_name(token_text);
343 if (spec_stack[spec_sp].value == -1)
344 { spec_stack[spec_sp].value = 0;
345 ebf_error("action (or fake action) name", token_text);
349 spec_stack[spec_sp] =
350 code_generate(parse_expression(CONSTANT_CONTEXT), CONSTANT_CONTEXT, -1);
352 misc_keywords.enabled = TRUE;
354 misc_keywords.enabled = FALSE;
356 spec_type[spec_sp++] = switch_sign();
357 switch(spec_type[spec_sp-1])
360 ebf_error("',' or ':'", token_text);
361 else ebf_error("',', ':' or 'to'", token_text);
362 panic_mode_error_recovery();
364 case 1: goto GenSpecCode;
365 case 3: if (label_after == -1) label_after = next_label++;
371 if ((spec_sp > max_equality_args) && (label_after == -1))
372 label_after = next_label++;
374 if (label_after == -1)
375 { compile_alternatives(switch_value, spec_sp, 0, label, FALSE); return;
378 for (i=0; i<spec_sp;)
380 j=i; while ((j<spec_sp) && (spec_type[j] != 3)) j++;
383 { if (j-i > max_equality_args) j=i+max_equality_args;
386 compile_alternatives(switch_value, j-i, i, label, FALSE);
388 compile_alternatives(switch_value, j-i, i, label_after, TRUE);
395 if (i == spec_sp - 2)
396 { assemblez_2_branch(jl_zc, switch_value, spec_stack[i],
398 assemblez_2_branch(jg_zc, switch_value, spec_stack[i+1],
402 { assemblez_2_branch(jl_zc, switch_value, spec_stack[i],
404 assemblez_2_branch(jg_zc, switch_value, spec_stack[i+1],
406 assemble_label_no(next_label++);
410 if (i == spec_sp - 2)
411 { assembleg_2_branch(jlt_gc, switch_value, spec_stack[i],
413 assembleg_2_branch(jgt_gc, switch_value, spec_stack[i+1],
417 { assembleg_2_branch(jlt_gc, switch_value, spec_stack[i],
419 assembleg_2_branch(jle_gc, switch_value, spec_stack[i+1],
421 assemble_label_no(next_label++);
428 assemble_label_no(label_after);
431 extern int32 parse_routine(char *source, int embedded_flag, char *name,
432 int veneer_flag, int r_symbol)
433 { int32 packed_address; int i; int debug_flag = FALSE;
434 int switch_clause_made = FALSE, default_clause_made = FALSE,
436 debug_location_beginning beginning_debug_location =
437 get_token_location_beginning();
439 /* (switch_label needs no initialisation here, but it prevents some
440 compilers from issuing warnings) */
442 if ((source != lexical_source) || (veneer_flag))
443 { lexical_source = source;
444 restart_lexer(lexical_source, name);
449 for (i=0;i<MAX_LOCAL_VARIABLES-1;i++) local_variables.keywords[i] = "";
452 { statements.enabled = TRUE;
453 dont_enter_into_symbol_table = TRUE;
455 dont_enter_into_symbol_table = FALSE;
456 if ((token_type == SEP_TT) && (token_value == TIMES_SEP)
457 && (no_locals == 0) && (!debug_flag))
458 { debug_flag = TRUE; continue;
461 if (token_type != DQ_TT)
462 { if ((token_type == SEP_TT)
463 && (token_value == SEMICOLON_SEP)) break;
464 ebf_error("local variable name or ';'", token_text);
465 panic_mode_error_recovery();
469 if (strlen(token_text) > MAX_IDENTIFIER_LENGTH)
470 { error_named("Local variable identifier too long:", token_text);
471 panic_mode_error_recovery();
475 if (no_locals == MAX_LOCAL_VARIABLES-1)
476 { error_numbered("Too many local variables for a routine; max is",
477 MAX_LOCAL_VARIABLES-1);
478 panic_mode_error_recovery();
482 for (i=0;i<no_locals;i++)
483 if (strcmpcis(token_text, local_variables.keywords[i])==0)
484 error_named("Local variable defined twice:", token_text);
485 local_variables.keywords[no_locals++] = token_text;
488 construct_local_variable_tables();
490 if ((trace_fns_setting==3)
491 || ((trace_fns_setting==2) && (veneer_mode==FALSE))
492 || ((trace_fns_setting==1) && (is_systemfile()==FALSE)))
494 if ((embedded_flag == FALSE) && (veneer_mode == FALSE) && debug_flag)
495 sflags[r_symbol] |= STAR_SFLAG;
497 packed_address = assemble_routine_header(no_locals, debug_flag,
498 name, embedded_flag, r_symbol);
501 { begin_syntax_line(TRUE);
505 if (token_type == EOF_TT)
506 { ebf_error("']'", token_text);
509 get_token_location_end(beginning_debug_location));
514 if ((token_type == SEP_TT)
515 && (token_value == CLOSE_SQUARE_SEP))
516 { if (switch_clause_made && (!default_clause_made))
517 assemble_label_no(switch_label);
518 directives.enabled = TRUE;
519 sequence_point_follows = TRUE;
523 get_token_location_end(beginning_debug_location));
528 if ((token_type == STATEMENT_TT) && (token_value == SDEFAULT_CODE))
529 { if (default_clause_made)
530 error("Multiple 'default' clauses defined in same 'switch'");
531 default_clause_made = TRUE;
533 if (switch_clause_made)
534 { if (!execution_never_reaches_here)
535 { sequence_point_follows = FALSE;
537 assemblez_0((embedded_flag)?rfalse_zc:rtrue_zc);
539 assembleg_1(return_gc,
540 ((embedded_flag)?zero_operand:one_operand));
542 assemble_label_no(switch_label);
544 switch_clause_made = TRUE;
547 if ((token_type == SEP_TT) &&
548 (token_value == COLON_SEP)) continue;
549 ebf_error("':' after 'default'", token_text);
550 panic_mode_error_recovery();
554 /* Only check for the form of a case switch if the initial token
555 isn't double-quoted text, as that would mean it was a print_ret
556 statement: this is a mild ambiguity in the grammar.
557 Action statements also cannot be cases. */
559 if ((token_type != DQ_TT) && (token_type != SEP_TT))
561 if (switch_sign() > 0)
562 { assembly_operand AO;
563 if (default_clause_made)
564 error("'default' must be the last 'switch' case");
566 if (switch_clause_made)
567 { if (!execution_never_reaches_here)
568 { sequence_point_follows = FALSE;
570 assemblez_0((embedded_flag)?rfalse_zc:rtrue_zc);
572 assembleg_1(return_gc,
573 ((embedded_flag)?zero_operand:one_operand));
575 assemble_label_no(switch_label);
578 switch_label = next_label++;
579 switch_clause_made = TRUE;
580 put_token_back(); put_token_back();
583 INITAOTV(&AO, VARIABLE_OT, 249);
586 INITAOTV(&AO, GLOBALVAR_OT, MAX_LOCAL_VARIABLES+6); /* sw__var */
588 parse_switch_spec(AO, switch_label, TRUE);
593 { put_token_back(); put_token_back(); get_next_token();
594 sequence_point_follows = TRUE;
598 parse_statement(-1, -1);
602 return packed_address;
605 extern void parse_code_block(int break_label, int continue_label,
607 { int switch_clause_made = FALSE, default_clause_made = FALSE, switch_label = 0,
610 begin_syntax_line(TRUE);
613 if (token_type == SEP_TT && token_value == OPEN_BRACE_SEP)
615 { begin_syntax_line(TRUE);
617 if (token_type == SEP_TT && token_value == CLOSE_BRACE_SEP)
618 { if (switch_clause_made && (!default_clause_made))
619 assemble_label_no(switch_label);
622 if (token_type == EOF_TT)
623 { ebf_error("'}'", token_text); return; }
625 if (switch_rule != 0)
627 /* Within a 'switch' block */
629 if ((token_type==STATEMENT_TT)&&(token_value==SDEFAULT_CODE))
630 { if (default_clause_made)
631 error("Multiple 'default' clauses defined in same 'switch'");
632 default_clause_made = TRUE;
634 if (switch_clause_made)
635 { if (!execution_never_reaches_here)
636 { sequence_point_follows = FALSE;
637 assemble_jump(break_label);
639 assemble_label_no(switch_label);
641 switch_clause_made = TRUE;
644 if ((token_type == SEP_TT) &&
645 (token_value == COLON_SEP)) continue;
646 ebf_error("':' after 'default'", token_text);
647 panic_mode_error_recovery();
651 /* Decide: is this an ordinary statement, or the start
654 if (token_type == DQ_TT) goto NotASwitchCase;
657 = ((token_type == SEP_TT)&&(token_value == MINUS_SEP));
658 if (unary_minus_flag) get_next_token();
660 /* Now read the token _after_ any possible constant:
661 if that's a 'to', ',' or ':' then we have a case */
663 misc_keywords.enabled = TRUE;
665 misc_keywords.enabled = FALSE;
667 if (switch_sign() > 0)
668 { assembly_operand AO;
670 if (default_clause_made)
671 error("'default' must be the last 'switch' case");
673 if (switch_clause_made)
674 { if (!execution_never_reaches_here)
675 { sequence_point_follows = FALSE;
676 assemble_jump(break_label);
678 assemble_label_no(switch_label);
681 switch_label = next_label++;
682 switch_clause_made = TRUE;
683 put_token_back(); put_token_back();
684 if (unary_minus_flag) put_token_back();
687 parse_switch_spec(AO, switch_label, FALSE);
691 { put_token_back(); put_token_back();
692 if (unary_minus_flag) put_token_back();
697 if ((switch_rule != 0) && (!switch_clause_made))
698 ebf_error("switch value", token_text);
701 sequence_point_follows = TRUE;
702 parse_statement(break_label, continue_label);
707 if (switch_rule != 0)
708 ebf_error("braced code block after 'switch'", token_text);
710 parse_statement(break_label, continue_label);
714 /* ========================================================================= */
715 /* Data structure management routines */
716 /* ------------------------------------------------------------------------- */
718 extern void init_syntax_vars(void)
722 extern void syntax_begin_pass(void)
723 { no_syntax_lines = 0;
726 extern void syntax_allocate_arrays(void)
730 extern void syntax_free_arrays(void)
734 /* ========================================================================= */