glcpp: Support line continuations within preprocessor directives.
authorKenneth Graunke <kenneth@whitecape.org>
Wed, 23 Jun 2010 19:31:09 +0000 (12:31 -0700)
committerIan Romanick <ian.d.romanick@intel.com>
Wed, 23 Jun 2010 21:14:57 +0000 (14:14 -0700)
Fixes CorrectPreprocess5.frag.

glcpp/pp.c

index 5455518..a25b7b7 100644 (file)
@@ -21,6 +21,8 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <assert.h>
+#include <ctype.h>
 #include "glcpp.h"
 
 void
@@ -56,11 +58,88 @@ glcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
        parser->info_log = talloc_strdup_append(parser->info_log, "\n");
 }
 
+/* Searches backwards for '^ *#' from a given starting point. */
+static int
+in_directive(const char *shader, const char *ptr)
+{
+       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;
+       }
+       return 0;
+}
+
+/* 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.
+ */
+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;
+               /* Find the preceding '\', if it exists */
+               if (newline[-1] == '\\') {
+                       backslash = newline - 1;
+               } else if (newline[-1] == '\r' && newline[-2] == '\\') {
+                       backslash = newline - 2;
+               }
+               /* Double backslashes don't count (the backslash is escaped) */
+               if (backslash != NULL && backslash[-1] == '\\') {
+                       backslash = NULL;
+               }
+
+               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;
+                               }
+                       }
+                       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;
+       }
+       clean = talloc_strdup_append(clean, shader);
+       return clean;
+}
+
 extern int
 preprocess(void *talloc_ctx, const char **shader, char **info_log)
 {
        int errors;
        glcpp_parser_t *parser = glcpp_parser_create ();
+       *shader = remove_line_continuations(parser, *shader);
+
        glcpp_lex_set_source_string (parser, *shader);
 
        glcpp_parser_parse (parser);