nir: move to compiler/
[mesa.git] / src / glsl / glcpp / pp.c
index 8769f4f7e55a1de926faa939139851962b435139..160c6662ff6fa9e7266967c44efb71f420df2f22 100644 (file)
@@ -25,7 +25,6 @@
 #include <string.h>
 #include <ctype.h>
 #include "glcpp.h"
-#include "main/core.h" /* for isblank() on MSVC */
 
 void
 glcpp_error (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
@@ -33,16 +32,20 @@ glcpp_error (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
        va_list ap;
 
        parser->error = 1;
-       parser->info_log = talloc_asprintf_append(parser->info_log,
-                                                 "%u:%u(%u): "
-                                                 "preprocessor error: ",
-                                                 locp->source,
-                                                 locp->first_line,
-                                                 locp->first_column);
+       ralloc_asprintf_rewrite_tail(&parser->info_log,
+                                    &parser->info_log_length,
+                                    "%u:%u(%u): "
+                                    "preprocessor error: ",
+                                    locp->source,
+                                    locp->first_line,
+                                    locp->first_column);
        va_start(ap, fmt);
-       parser->info_log = talloc_vasprintf_append(parser->info_log, fmt, ap);
+       ralloc_vasprintf_rewrite_tail(&parser->info_log,
+                                     &parser->info_log_length,
+                                     fmt, ap);
        va_end(ap);
-       parser->info_log = talloc_strdup_append(parser->info_log, "\n");
+       ralloc_asprintf_rewrite_tail(&parser->info_log,
+                                    &parser->info_log_length, "\n");
 }
 
 void
@@ -50,104 +53,173 @@ glcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
 {
        va_list ap;
 
-       parser->info_log = talloc_asprintf_append(parser->info_log,
-                                                 "%u:%u(%u): "
-                                                 "preprocessor warning: ",
-                                                 locp->source,
-                                                 locp->first_line,
-                                                 locp->first_column);
+       ralloc_asprintf_rewrite_tail(&parser->info_log,
+                                    &parser->info_log_length,
+                                    "%u:%u(%u): "
+                                    "preprocessor warning: ",
+                                    locp->source,
+                                    locp->first_line,
+                                    locp->first_column);
        va_start(ap, fmt);
-       parser->info_log = talloc_vasprintf_append(parser->info_log, fmt, ap);
+       ralloc_vasprintf_rewrite_tail(&parser->info_log,
+                                     &parser->info_log_length,
+                                     fmt, ap);
        va_end(ap);
-       parser->info_log = talloc_strdup_append(parser->info_log, "\n");
+       ralloc_asprintf_rewrite_tail(&parser->info_log,
+                                    &parser->info_log_length, "\n");
 }
 
-/* Searches backwards for '^ *#' from a given starting point. */
-static int
-in_directive(const char *shader, const char *ptr)
+/* Given str, (that's expected to start with a newline terminator of some
+ * sort), return a pointer to the first character in str after the newline.
+ *
+ * A newline terminator can be any of the following sequences:
+ *
+ *     "\r\n"
+ *     "\n\r"
+ *     "\n"
+ *     "\r"
+ *
+ * And the longest such sequence will be skipped.
+ */
+static const char *
+skip_newline (const char *str)
 {
-       assert(ptr >= shader);
-
-       /* Search backwards for '#'. If we find a \n first, it doesn't count */
-       for (; ptr >= shader && *ptr != '#'; ptr--) {
-               if (*ptr == '\n')
-                       return 0;
-       }
-       if (ptr >= shader) {
-               /* Found '#'...look for spaces preceded by a newline */
-               for (ptr--; ptr >= shader && isblank(*ptr); ptr--);
-               // FIXME: I don't think the '\n' case can happen
-               if (ptr < shader || *ptr == '\n')
-                       return 1;
+       const char *ret = str;
+
+       if (ret == NULL)
+               return ret;
+
+       if (*ret == '\0')
+               return ret;
+
+       if (*ret == '\r') {
+               ret++;
+               if (*ret && *ret == '\n')
+                       ret++;
+       } else if (*ret == '\n') {
+               ret++;
+               if (*ret && *ret == '\r')
+                       ret++;
        }
-       return 0;
+
+       return ret;
 }
 
-/* Remove any line continuation characters in preprocessing directives.
- * However, ignore any in GLSL code, as "There is no line continuation
- * character" (1.30 page 9) in GLSL.
+/* Remove any line continuation characters in the shader, (whether in
+ * preprocessing directives or in GLSL code).
  */
 static char *
 remove_line_continuations(glcpp_parser_t *ctx, const char *shader)
 {
-       int in_continued_line = 0;
-       int extra_newlines = 0;
-       char *clean = talloc_strdup(ctx, "");
-       const char *search_start = shader;
-       const char *newline;
-       while ((newline = strchr(search_start, '\n')) != NULL) {
-               const char *backslash = NULL;
-
-               /* # of characters preceding the newline. */
-               int n = newline - shader;
-
-               /* Find the preceding '\', if it exists */
-               if (n >= 1 && newline[-1] == '\\')
-                       backslash = newline - 1;
-               else if (n >= 2 && newline[-1] == '\r' && newline[-2] == '\\')
-                       backslash = newline - 2;
-
-               /* Double backslashes don't count (the backslash is escaped) */
-               if (backslash != NULL && backslash[-1] == '\\') {
-                       backslash = NULL;
-               }
+       char *clean = ralloc_strdup(ctx, "");
+       const char *backslash, *newline, *search_start;
+        const char *cr, *lf;
+        char newline_separator[3];
+       int collapsed_newlines = 0;
+
+       search_start = shader;
+
+       /* Determine what flavor of newlines this shader is using. GLSL
+        * provides for 4 different possible ways to separate lines, (using
+        * one or two characters):
+        *
+        *      "\n" (line-feed, like Linux, Unix, and new Mac OS)
+        *      "\r" (carriage-return, like old Mac files)
+        *      "\r\n" (carriage-return + line-feed, like DOS files)
+        *      "\n\r" (line-feed + carriage-return, like nothing, really)
+        *
+        * This code explicitly supports a shader that uses a mixture of
+        * newline terminators and will properly handle line continuation
+        * backslashes followed by any of the above.
+        *
+        * But, since we must also insert additional newlines in the output
+        * (for any collapsed lines) we attempt to maintain consistency by
+        * examining the first encountered newline terminator, and using the
+        * same terminator for any newlines we insert.
+        */
+       cr = strchr(search_start, '\r');
+       lf = strchr(search_start, '\n');
+
+       newline_separator[0] = '\n';
+       newline_separator[1] = '\0';
+       newline_separator[2] = '\0';
+
+       if (cr == NULL) {
+               /* Nothing to do. */
+       } else if (lf == NULL) {
+               newline_separator[0] = '\r';
+       } else if (lf == cr + 1) {
+               newline_separator[0] = '\r';
+               newline_separator[1] = '\n';
+       } else if (cr == lf + 1) {
+               newline_separator[0] = '\n';
+               newline_separator[1] = '\r';
+       }
 
-               if (backslash != NULL) {
-                       /* We found a line continuation, but do we care? */
-                       if (!in_continued_line) {
-                               if (in_directive(shader, backslash)) {
-                                       in_continued_line = 1;
-                                       extra_newlines = 0;
+       while (true) {
+               backslash = strchr(search_start, '\\');
+
+               /* If we have previously collapsed any line-continuations,
+                * then we want to insert additional newlines at the next
+                * occurrence of a newline character to avoid changing any
+                * line numbers.
+                */
+               if (collapsed_newlines) {
+                       cr = strchr (search_start, '\r');
+                       lf = strchr (search_start, '\n');
+                       if (cr && lf)
+                               newline = cr < lf ? cr : lf;
+                       else if (cr)
+                               newline = cr;
+                       else
+                               newline = lf;
+                       if (newline &&
+                           (backslash == NULL || newline < backslash))
+                       {
+                               ralloc_strncat(&clean, shader,
+                                              newline - shader + 1);
+                               while (collapsed_newlines) {
+                                       ralloc_strcat(&clean, newline_separator);
+                                       collapsed_newlines--;
                                }
+                               shader = skip_newline (newline);
+                               search_start = shader;
                        }
-                       if (in_continued_line) {
-                               /* Copy everything before the \ */
-                               clean = talloc_strndup_append(clean, shader, backslash - shader);
-                               shader = newline + 1;
-                               extra_newlines++;
-                       }
-               } else if (in_continued_line) {
-                       /* Copy everything up to and including the \n */
-                       clean = talloc_strndup_append(clean, shader, newline - shader + 1);
-                       shader = newline + 1;
-                       /* Output extra newlines to make line numbers match */
-                       for (; extra_newlines > 0; extra_newlines--)
-                               clean = talloc_strdup_append(clean, "\n");
-                       in_continued_line = 0;
                }
-               search_start = newline + 1;
+
+               search_start = backslash + 1;
+
+               if (backslash == NULL)
+                       break;
+
+               /* At each line continuation, (backslash followed by a
+                * newline), copy all preceding text to the output, then
+                * advance the shader pointer to the character after the
+                * newline.
+                */
+               if (backslash[1] == '\r' || backslash[1] == '\n')
+               {
+                       collapsed_newlines++;
+                       ralloc_strncat(&clean, shader, backslash - shader);
+                       shader = skip_newline (backslash + 1);
+                       search_start = shader;
+               }
        }
-       clean = talloc_strdup_append(clean, shader);
+
+       ralloc_strcat(&clean, shader);
+
        return clean;
 }
 
-extern int
-preprocess(void *talloc_ctx, const char **shader, char **info_log,
-          const struct gl_extensions *extensions)
+int
+glcpp_preprocess(void *ralloc_ctx, const char **shader, char **info_log,
+          const struct gl_extensions *extensions, struct gl_context *gl_ctx)
 {
        int errors;
-       glcpp_parser_t *parser = glcpp_parser_create (extensions);
-       *shader = remove_line_continuations(parser, *shader);
+       glcpp_parser_t *parser = glcpp_parser_create (extensions, gl_ctx->API);
+
+       if (! gl_ctx->Const.DisableGLSLLineContinuations)
+               *shader = remove_line_continuations(parser, *shader);
 
        glcpp_lex_set_source_string (parser, *shader);
 
@@ -156,9 +228,11 @@ preprocess(void *talloc_ctx, const char **shader, char **info_log,
        if (parser->skip_stack)
                glcpp_error (&parser->skip_stack->loc, parser, "Unterminated #if\n");
 
-       *info_log = talloc_strdup_append(*info_log, parser->info_log);
+       glcpp_parser_resolve_implicit_version(parser);
+
+       ralloc_strcat(info_log, parser->info_log);
 
-       talloc_steal(talloc_ctx, parser->output);
+       ralloc_steal(ralloc_ctx, parser->output);
        *shader = parser->output;
 
        errors = parser->error;