Update to commit e33eef4f8fab800eaf4a32b2d159cde6c4bbb38e
[inform.git] / src / directs.c
index d3b7f32c6032e1f7548700b691f8ff3c7212a05a..15831728118b840102d401f7f67b7619c996a21e 100644 (file)
@@ -1,9 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "directs" : Directives (# commands)                                     */
 /*                                                                           */
-/* Copyright (c) Graham Nelson 1993 - 2018                                   */
-/*                                                                           */
-/* This file is part of Inform.                                              */
+/*   Part of Inform 6.35                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2021                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -35,7 +34,8 @@ brief_location routine_starts_line; /* Source code location where the current
 
 static int constant_made_yet;      /* Have any constants been defined yet?   */
 
-static int ifdef_stack[32], ifdef_sp;
+#define MAX_IFDEF_STACK (32)
+static int ifdef_stack[MAX_IFDEF_STACK], ifdef_sp;
 
 /* ------------------------------------------------------------------------- */
 
@@ -52,6 +52,14 @@ static int ebf_error_recover(char *s1, char *s2)
     return FALSE;
 }
 
+static int ebf_symbol_error_recover(char *s1, char *name, char *type, brief_location report_line)
+{
+    /* Same for ebf_symbol_error(). */
+    ebf_symbol_error(s1, name, type, report_line);
+    panic_mode_error_recovery();
+    return FALSE;
+}
+
 /* ------------------------------------------------------------------------- */
 
 extern int parse_given_directive(int internal_flag)
@@ -65,6 +73,26 @@ extern int parse_given_directive(int internal_flag)
     const char *constant_name;
     debug_location_beginning beginning_debug_location;
 
+    if (internal_flag)
+    {
+        /* Only certain directives, such as #ifdef, are permitted within
+           a routine or object definition. In older versions of Inform,
+           nearly any directive was accepted, but this was -- to quote
+           an old code comment -- "about as well-supported as Wile E. 
+           Coyote one beat before the plummet-lines kick in." */
+        
+        if (token_value != IFV3_CODE && token_value != IFV5_CODE
+            && token_value != IFDEF_CODE && token_value != IFNDEF_CODE
+            && token_value != IFTRUE_CODE && token_value != IFFALSE_CODE
+            && token_value != IFNOT_CODE && token_value != ENDIF_CODE
+            && token_value != MESSAGE_CODE && token_value != ORIGSOURCE_CODE
+            && token_value != TRACE_CODE) {
+            char *dirname = directives.keywords[token_value];
+            error_named("Cannot nest this directive inside a routine or object:", dirname);
+            panic_mode_error_recovery(); return FALSE;
+        }
+    }
+    
     switch(token_value)
     {
 
@@ -79,9 +107,8 @@ extern int parse_given_directive(int internal_flag)
            if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
                return FALSE;
 
-           /* Z-code has a 64-abbrev limit; Glulx doesn't. */
-           if (!glulx_mode && no_abbreviations==64)
-           {   error("All 64 abbreviations already declared");
+           if (!glulx_mode && no_abbreviations==96)
+           {   error("All 96 Z-machine abbreviations already declared");
                panic_mode_error_recovery(); return FALSE;
            }
            if (no_abbreviations==MAX_ABBREVS)
@@ -124,10 +151,6 @@ extern int parse_given_directive(int internal_flag)
     /* --------------------------------------------------------------------- */
 
     case CLASS_CODE: 
-        if (internal_flag)
-        {   error("Cannot nest #Class inside a routine or object");
-            panic_mode_error_recovery(); return FALSE;
-        }
         make_class(NULL);                                 /* See "objects.c" */
         return FALSE;
 
@@ -142,12 +165,16 @@ extern int parse_given_directive(int internal_flag)
         get_next_token(); i = token_value;
         beginning_debug_location = get_token_location_beginning();
 
-        if ((token_type != SYMBOL_TT)
-            || (!(sflags[i] & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG))))
+        if (token_type != SYMBOL_TT)
         {   discard_token_location(beginning_debug_location);
             return ebf_error_recover("new constant name", token_text);
         }
 
+        if (!(sflags[i] & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG)))
+        {   discard_token_location(beginning_debug_location);
+            return ebf_symbol_error_recover("new constant name", token_text, typename(stypes[i]), slines[i]);
+        }
+
         assign_symbol(i, 0, CONSTANT_T);
         constant_name = token_text;
 
@@ -289,7 +316,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
                 assembly_operand AO;
                 put_token_back();
                 AO = parse_expression(CONSTANT_CONTEXT);
-                if (module_switch && (AO.marker != 0))
+                if (AO.marker != 0)
                     error("A definite value must be given as a Dictionary flag");
                 else
                     val1 = AO.value;
@@ -302,7 +329,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
                     assembly_operand AO;
                     put_token_back();
                     AO = parse_expression(CONSTANT_CONTEXT);
-                    if (module_switch && (AO.marker != 0))
+                    if (AO.marker != 0)
                         error("A definite value must be given as a Dictionary flag");
                     else
                         val3 = AO.value;
@@ -434,17 +461,19 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         break;
 
     case IFV3_CODE:
-        flag = FALSE; if (version_number == 3) flag = TRUE;
+        flag = FALSE;
+        if (!glulx_mode && version_number <= 3) flag = TRUE;
         goto HashIfCondition;
 
     case IFV5_CODE:
-        flag = TRUE; if (version_number == 3) flag = FALSE;
+        flag = TRUE;
+        if (!glulx_mode && version_number <= 3) flag = FALSE;
         goto HashIfCondition;
 
     case IFTRUE_CODE:
         {   assembly_operand AO;
             AO = parse_expression(CONSTANT_CONTEXT);
-            if (module_switch && (AO.marker != 0))
+            if (AO.marker != 0)
             {   error("This condition can't be determined");
                 flag = 0;
             }
@@ -455,7 +484,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
     case IFFALSE_CODE:
         {   assembly_operand AO;
             AO = parse_expression(CONSTANT_CONTEXT);
-            if (module_switch && (AO.marker != 0))
+            if (AO.marker != 0)
             {   error("This condition can't be determined");
                 flag = 1;
             }
@@ -468,6 +497,11 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)))
             return ebf_error_recover("semicolon after 'If...' condition", token_text);
 
+        if (ifdef_sp >= MAX_IFDEF_STACK) {
+            error("'If' directives nested too deeply");
+            panic_mode_error_recovery(); return FALSE;
+        }
+        
         if (flag)
         {   ifdef_stack[ifdef_sp++] = TRUE; return FALSE; }
         else
@@ -579,15 +613,21 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
         {   error("'LowString' cannot be used in -M (Module) mode");
             panic_mode_error_recovery(); return FALSE;
         }
+        if (glulx_mode) {
+            error("The LowString directive has no meaning in Glulx.");
+            panic_mode_error_recovery(); return FALSE;
+        }
         get_next_token(); i = token_value;
-        if ((token_type != SYMBOL_TT) || (!(sflags[i] & UNKNOWN_SFLAG)))
+        if (token_type != SYMBOL_TT)
             return ebf_error_recover("new low string name", token_text);
+        if (!(sflags[i] & UNKNOWN_SFLAG))
+            return ebf_symbol_error_recover("new low string name", token_text, typename(stypes[i]), slines[i]);
 
         get_next_token();
         if (token_type != DQ_TT)
             return ebf_error_recover("literal string in double-quotes", token_text);
 
-        assign_symbol(i, compile_string(token_text, TRUE, TRUE), CONSTANT_T);
+        assign_symbol(i, compile_string(token_text, STRCTX_LOWSTRING), CONSTANT_T);
         break;
 
     /* --------------------------------------------------------------------- */
@@ -643,10 +683,6 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
     /* --------------------------------------------------------------------- */
 
     case NEARBY_CODE:
-        if (internal_flag)
-        {   error("Cannot nest #Nearby inside a routine or object");
-            panic_mode_error_recovery(); return FALSE;
-        }
         make_object(TRUE, NULL, -1, -1, -1);
         return FALSE;                                     /* See "objects.c" */
 
@@ -655,10 +691,6 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
     /* --------------------------------------------------------------------- */
 
     case OBJECT_CODE:
-        if (internal_flag)
-        {   error("Cannot nest #Object inside a routine or object");
-            panic_mode_error_recovery(); return FALSE;
-        }
         make_object(FALSE, NULL, -1, -1, -1);
         return FALSE;                                     /* See "objects.c" */
 
@@ -741,7 +773,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
     case RELEASE_CODE:
         {   assembly_operand AO;
             AO = parse_expression(CONSTANT_CONTEXT);
-            if (module_switch && (AO.marker != 0))
+            if (AO.marker != 0)
                 error("A definite value must be given as release number");
             else
                 release_number = AO.value;
@@ -849,11 +881,6 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)");
     /* --------------------------------------------------------------------- */
 
     case STUB_CODE:
-        if (internal_flag)
-        {   error("Cannot nest #Stub inside a routine or object");
-            panic_mode_error_recovery(); return FALSE;
-        }
-
         /* The upcoming symbol is a definition; don't count it as a
            top-level reference *to* the stub function. */
         df_dont_note_global_symbols = TRUE;
@@ -1060,7 +1087,7 @@ the first constant definition");
               break;
             }
 
-            if (module_switch && (AO.marker != 0))
+            if (AO.marker != 0)
                 error("A definite value must be given as version number");
             else 
             if (glulx_mode)