diff options
Diffstat (limited to 'src/compiler/glsl/glcpp/pp.c')
-rw-r--r-- | src/compiler/glsl/glcpp/pp.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/src/compiler/glsl/glcpp/pp.c b/src/compiler/glsl/glcpp/pp.c new file mode 100644 index 00000000000..160c6662ff6 --- /dev/null +++ b/src/compiler/glsl/glcpp/pp.c @@ -0,0 +1,241 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include "glcpp.h" + +void +glcpp_error (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...) +{ + va_list ap; + + parser->error = 1; + 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); + ralloc_vasprintf_rewrite_tail(&parser->info_log, + &parser->info_log_length, + fmt, ap); + va_end(ap); + ralloc_asprintf_rewrite_tail(&parser->info_log, + &parser->info_log_length, "\n"); +} + +void +glcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...) +{ + va_list ap; + + 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); + ralloc_vasprintf_rewrite_tail(&parser->info_log, + &parser->info_log_length, + fmt, ap); + va_end(ap); + ralloc_asprintf_rewrite_tail(&parser->info_log, + &parser->info_log_length, "\n"); +} + +/* 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) +{ + 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 ret; +} + +/* 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) +{ + 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'; + } + + 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; + } + } + + 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; + } + } + + ralloc_strcat(&clean, shader); + + return clean; +} + +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, gl_ctx->API); + + if (! gl_ctx->Const.DisableGLSLLineContinuations) + *shader = remove_line_continuations(parser, *shader); + + glcpp_lex_set_source_string (parser, *shader); + + glcpp_parser_parse (parser); + + if (parser->skip_stack) + glcpp_error (&parser->skip_stack->loc, parser, "Unterminated #if\n"); + + glcpp_parser_resolve_implicit_version(parser); + + ralloc_strcat(info_log, parser->info_log); + + ralloc_steal(ralloc_ctx, parser->output); + *shader = parser->output; + + errors = parser->error; + glcpp_parser_destroy (parser); + return errors; +} |