Update to commit a469d404a7dc4e87e18f367eb4d8e05fc32d20a7
[inform.git] / src / syntax.c
index 982d49f101ff934383b19d180d42ededd6f73600..f99de050b87089721355ca52cd7e597df316f608 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "syntax" : Syntax analyser and compiler                                 */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -15,7 +15,7 @@
 /* GNU General Public License for more details.                              */
 /*                                                                           */
 /* You should have received a copy of the GNU General Public License         */
-/* along with Inform. If not, see https://gnu.org/licenses/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -146,9 +146,9 @@ extern void parse_program(char *source)
 
 extern int parse_directive(int internal_flag)
 {
-    /*  Internal_flag is FALSE if the directive is encountered normally,
-        TRUE if encountered with a # prefix inside a routine or object
-        definition.
+    /*  Internal_flag is FALSE if the directive is encountered normally
+        (at the top level of the program); TRUE if encountered with 
+        a # prefix inside a routine or object definition.
 
         (Only directives like #ifdef are permitted inside a definition.)
 
@@ -158,6 +158,11 @@ extern int parse_directive(int internal_flag)
     int is_renamed;
 
     begin_syntax_line(FALSE);
+    if (!internal_flag) {
+        /* An internal directive can occur in the middle of an expression or
+           object definition. So we only release for top-level directives.   */
+        release_token_texts();
+    }
     get_next_token();
 
     if (token_type == EOF_TT) return(FALSE);
@@ -184,9 +189,9 @@ extern int parse_directive(int internal_flag)
         {   ebf_error("routine name", token_text);
             return(FALSE);
         }
-        if ((!(sflags[token_value] & UNKNOWN_SFLAG))
-            && (!(sflags[token_value] & REPLACE_SFLAG)))
-        {   ebf_symbol_error("routine name", token_text, typename(stypes[token_value]), slines[token_value]);
+        if ((!(symbols[token_value].flags & UNKNOWN_SFLAG))
+            && (!(symbols[token_value].flags & REPLACE_SFLAG)))
+        {   ebf_symbol_error("routine name", token_text, typename(symbols[token_value].type), symbols[token_value].line);
             return(FALSE);
         }
 
@@ -195,7 +200,7 @@ extern int parse_directive(int internal_flag)
         rep_symbol = routine_symbol;
         is_renamed = find_symbol_replacement(&rep_symbol);
 
-        if ((sflags[routine_symbol] & REPLACE_SFLAG) 
+        if ((symbols[routine_symbol].flags & REPLACE_SFLAG) 
             && !is_renamed && (is_systemfile()))
         {   /* The function is definitely being replaced (system_file
                always loses priority in a replacement) but is not
@@ -214,9 +219,9 @@ extern int parse_directive(int internal_flag)
         {   /* Parse the function definition and assign its symbol. */
             assign_symbol(routine_symbol,
                 parse_routine(lexical_source, FALSE,
-                    (char *) symbs[routine_symbol], FALSE, routine_symbol),
+                    symbols[routine_symbol].name, FALSE, routine_symbol),
                 ROUTINE_T);
-            slines[routine_symbol] = routine_starts_line;
+            symbols[routine_symbol].line = routine_starts_line;
         }
 
         if (is_renamed) {
@@ -224,8 +229,8 @@ extern int parse_directive(int internal_flag)
                The first time we see a definition for symbol X, we
                copy it to Y -- that's the "original" form of the
                function. */
-            if (svals[rep_symbol] == 0) {
-                assign_symbol(rep_symbol, svals[routine_symbol], ROUTINE_T);
+            if (symbols[rep_symbol].value == 0) {
+                assign_symbol(rep_symbol, symbols[routine_symbol].value, ROUTINE_T);
             }
         }
 
@@ -237,13 +242,13 @@ extern int parse_directive(int internal_flag)
         return TRUE;
     }
 
-    if ((token_type == SYMBOL_TT) && (stypes[token_value] == CLASS_T))
+    if ((token_type == SYMBOL_TT) && (symbols[token_value].type == CLASS_T))
     {   if (internal_flag)
         {   error("It is illegal to nest an object in a routine using '#classname'");
             return(TRUE);
         }
-        sflags[token_value] |= USED_SFLAG;
-        make_object(FALSE, NULL, -1, -1, svals[token_value]);
+        symbols[token_value].flags |= USED_SFLAG;
+        make_object(FALSE, NULL, -1, -1, symbols[token_value].value);
         return TRUE;
     }
 
@@ -261,6 +266,7 @@ extern int parse_directive(int internal_flag)
     return !(parse_given_directive(internal_flag));
 }
 
+/* Check what's coming up after a switch case value. */
 static int switch_sign(void)
 {
     if ((token_type == SEP_TT)&&(token_value == COLON_SEP))   return 1;
@@ -269,8 +275,10 @@ static int switch_sign(void)
     return 0;
 }
 
-static assembly_operand spec_stack[32];
-static int spec_type[32];
+/* Info for the current switch statement. Both arrays indexed by spec_sp */
+#define MAX_SPEC_STACK (32)
+static assembly_operand spec_stack[MAX_SPEC_STACK];
+static int spec_type[MAX_SPEC_STACK];
 
 static void compile_alternatives_z(assembly_operand switch_value, int n,
     int stack_level, int label, int flag)
@@ -324,7 +332,7 @@ static void parse_switch_spec(assembly_operand switch_value, int label,
     sequence_point_follows = FALSE;
 
     do
-    {   if (spec_sp == 32)
+    {   if (spec_sp >= MAX_SPEC_STACK)
         {   error("At most 32 values can be given in a single 'switch' case");
             panic_mode_error_recovery();
             return;
@@ -444,7 +452,8 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name,
 
     no_locals = 0;
 
-    for (i=0;i<MAX_LOCAL_VARIABLES-1;i++) local_variables.keywords[i] = "";
+    for (i=0;i<MAX_LOCAL_VARIABLES-1;i++)
+        local_variable_names[i].text[0] = 0;
 
     do
     {   statements.enabled = TRUE;
@@ -477,12 +486,15 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name,
             break;
         }
 
-        for (i=0;i<no_locals;i++)
-            if (strcmpcis(token_text, local_variables.keywords[i])==0)
+        for (i=0;i<no_locals;i++) {
+            if (strcmpcis(token_text, local_variable_names[i].text)==0)
                 error_named("Local variable defined twice:", token_text);
-        local_variables.keywords[no_locals++] = token_text;
+        }
+        strcpy(local_variable_names[no_locals++].text, token_text);
     } while(TRUE);
 
+    /* Set up the local variable hash and the local_variables.keywords
+       table. */
     construct_local_variable_tables();
 
     if ((trace_fns_setting==3)
@@ -490,14 +502,14 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name,
         || ((trace_fns_setting==1) && (is_systemfile()==FALSE)))
         debug_flag = TRUE;
     if ((embedded_flag == FALSE) && (veneer_mode == FALSE) && debug_flag)
-        sflags[r_symbol] |= STAR_SFLAG;
+        symbols[r_symbol].flags |= STAR_SFLAG;
 
     packed_address = assemble_routine_header(no_locals, debug_flag,
         name, embedded_flag, r_symbol);
 
     do
     {   begin_syntax_line(TRUE);
-
+        release_token_texts();
         get_next_token();
 
         if (token_type == EOF_TT)
@@ -600,17 +612,36 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name,
     return packed_address;
 }
 
+/* Parse one block of code (a statement or brace-delimited stanza).
+   This is used by the IF, DO, FOR, OBJECTLOOP, SWITCH, and WHILE
+   statements.
+   (Note that this is *not* called by the top-level parse_routine() 
+   handler.)
+   The break_label and continue_label arguments are the labels in
+   the calling block to jump to on "break" or "continue". -1 means
+   we can't "break"/"continue" here (because we're not in a loop/switch).
+   If switch_rule is true, we're in a switch block; case labels are
+   accepted.
+*/
 extern void parse_code_block(int break_label, int continue_label,
     int switch_rule)
-{   int switch_clause_made = FALSE, default_clause_made = FALSE, switch_label = 0,
-        unary_minus_flag;
+{   int switch_clause_made = FALSE, default_clause_made = FALSE, switch_label = 0;
+    int unary_minus_flag, saved_entire_flag;
+
+    saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
+    if (execution_never_reaches_here)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
 
     begin_syntax_line(TRUE);
+    release_token_texts();
     get_next_token();
 
     if (token_type == SEP_TT && token_value == OPEN_BRACE_SEP)
-    {   do
+    {
+        /* Parse a braced stanza of statements. */
+        do
         {   begin_syntax_line(TRUE);
+            release_token_texts();
             get_next_token();
             
             if ((token_type == SEP_TT) && (token_value == HASH_SEP))
@@ -620,10 +651,12 @@ extern void parse_code_block(int break_label, int continue_label,
             if (token_type == SEP_TT && token_value == CLOSE_BRACE_SEP)
             {   if (switch_clause_made && (!default_clause_made))
                     assemble_label_no(switch_label);
-                return;
+                break;
             }
             if (token_type == EOF_TT)
-            {   ebf_error("'}'", token_text); return; }
+            {   ebf_error("'}'", token_text);
+                break;
+            }
 
             if (switch_rule != 0)
             {
@@ -706,12 +739,18 @@ extern void parse_code_block(int break_label, int continue_label,
         }
         while(TRUE);
     }
+    else {
+        if (switch_rule != 0)
+            ebf_error("braced code block after 'switch'", token_text);
+        
+        /* Parse a single statement. */
+        parse_statement(break_label, continue_label);
+    }
 
-    if (switch_rule != 0)
-        ebf_error("braced code block after 'switch'", token_text);
-
-    parse_statement(break_label, continue_label);
-    return;
+    if (saved_entire_flag)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    else
+        execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
 }
 
 /* ========================================================================= */